Merge branch 'develop' into bugfix/OP-3022-Look-publishing-and-srgb-colorspace-in-Maya-2022
|
|
@ -702,6 +702,37 @@ class ClipLoader(LoaderPlugin):
|
|||
|
||||
_mapping = None
|
||||
|
||||
def apply_settings(cls, project_settings, system_settings):
|
||||
|
||||
plugin_type_settings = (
|
||||
project_settings
|
||||
.get("flame", {})
|
||||
.get("load", {})
|
||||
)
|
||||
|
||||
if not plugin_type_settings:
|
||||
return
|
||||
|
||||
plugin_name = cls.__name__
|
||||
|
||||
plugin_settings = None
|
||||
# Look for plugin settings in host specific settings
|
||||
if plugin_name in plugin_type_settings:
|
||||
plugin_settings = plugin_type_settings[plugin_name]
|
||||
|
||||
if not plugin_settings:
|
||||
return
|
||||
|
||||
print(">>> We have preset for {}".format(plugin_name))
|
||||
for option, value in plugin_settings.items():
|
||||
if option == "enabled" and value is False:
|
||||
print(" - is disabled by preset")
|
||||
elif option == "representations":
|
||||
continue
|
||||
else:
|
||||
print(" - setting `{}`: `{}`".format(option, value))
|
||||
setattr(cls, option, value)
|
||||
|
||||
def get_colorspace(self, context):
|
||||
"""Get colorspace name
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@ import flame
|
|||
from pprint import pformat
|
||||
import openpype.hosts.flame.api as opfapi
|
||||
from openpype.lib import StringTemplate
|
||||
from openpype.lib.transcoding import (
|
||||
VIDEO_EXTENSIONS,
|
||||
IMAGE_EXTENSIONS
|
||||
)
|
||||
|
||||
|
||||
class LoadClip(opfapi.ClipLoader):
|
||||
|
|
@ -14,7 +18,10 @@ class LoadClip(opfapi.ClipLoader):
|
|||
"""
|
||||
|
||||
families = ["render2d", "source", "plate", "render", "review"]
|
||||
representations = ["exr", "dpx", "jpg", "jpeg", "png", "h264"]
|
||||
representations = ["*"]
|
||||
extensions = set(
|
||||
ext.lstrip(".") for ext in IMAGE_EXTENSIONS.union(VIDEO_EXTENSIONS)
|
||||
)
|
||||
|
||||
label = "Load as clip"
|
||||
order = -10
|
||||
|
|
|
|||
|
|
@ -4,7 +4,10 @@ import flame
|
|||
from pprint import pformat
|
||||
import openpype.hosts.flame.api as opfapi
|
||||
from openpype.lib import StringTemplate
|
||||
|
||||
from openpype.lib.transcoding import (
|
||||
VIDEO_EXTENSIONS,
|
||||
IMAGE_EXTENSIONS
|
||||
)
|
||||
|
||||
class LoadClipBatch(opfapi.ClipLoader):
|
||||
"""Load a subset to timeline as clip
|
||||
|
|
@ -14,7 +17,10 @@ class LoadClipBatch(opfapi.ClipLoader):
|
|||
"""
|
||||
|
||||
families = ["render2d", "source", "plate", "render", "review"]
|
||||
representations = ["exr", "dpx", "jpg", "jpeg", "png", "h264"]
|
||||
representations = ["*"]
|
||||
extensions = set(
|
||||
ext.lstrip(".") for ext in IMAGE_EXTENSIONS.union(VIDEO_EXTENSIONS)
|
||||
)
|
||||
|
||||
label = "Load as clip to current batch"
|
||||
order = -10
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ class FusionSetFrameRangeLoader(load.LoaderPlugin):
|
|||
"pointcache",
|
||||
"render"]
|
||||
representations = ["*"]
|
||||
extensions = {"*"}
|
||||
|
||||
label = "Set frame range"
|
||||
order = 11
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ class FusionLoadAlembicMesh(load.LoaderPlugin):
|
|||
"""Load Alembic mesh into Fusion"""
|
||||
|
||||
families = ["pointcache", "model"]
|
||||
representations = ["abc"]
|
||||
representations = ["*"]
|
||||
extensions = {"abc"}
|
||||
|
||||
label = "Load alembic mesh"
|
||||
order = -10
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@ class FusionLoadFBXMesh(load.LoaderPlugin):
|
|||
"""Load FBX mesh into Fusion"""
|
||||
|
||||
families = ["*"]
|
||||
representations = ["fbx"]
|
||||
representations = ["*"]
|
||||
extensions = {"fbx"}
|
||||
|
||||
label = "Load FBX mesh"
|
||||
order = -10
|
||||
|
|
|
|||
|
|
@ -12,6 +12,10 @@ from openpype.hosts.fusion.api import (
|
|||
get_current_comp,
|
||||
comp_lock_and_undo_chunk
|
||||
)
|
||||
from openpype.lib.transcoding import (
|
||||
IMAGE_EXTENSIONS,
|
||||
VIDEO_EXTENSIONS
|
||||
)
|
||||
|
||||
comp = get_current_comp()
|
||||
|
||||
|
|
@ -129,6 +133,9 @@ class FusionLoadSequence(load.LoaderPlugin):
|
|||
|
||||
families = ["imagesequence", "review", "render", "plate"]
|
||||
representations = ["*"]
|
||||
extensions = set(
|
||||
ext.lstrip(".") for ext in IMAGE_EXTENSIONS.union(VIDEO_EXTENSIONS)
|
||||
)
|
||||
|
||||
label = "Load sequence"
|
||||
order = -10
|
||||
|
|
|
|||
|
|
@ -20,8 +20,9 @@ class ImageSequenceLoader(load.LoaderPlugin):
|
|||
Stores the imported asset in a container named after the asset.
|
||||
"""
|
||||
|
||||
families = ["shot", "render", "image", "plate", "reference"]
|
||||
representations = ["jpeg", "png", "jpg"]
|
||||
families = ["shot", "render", "image", "plate", "reference", "review"]
|
||||
representations = ["*"]
|
||||
extensions = {"jpeg", "png", "jpg"}
|
||||
|
||||
def load(self, context, name=None, namespace=None, data=None):
|
||||
"""Plugin entry point.
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@ from openpype.pipeline import (
|
|||
legacy_io,
|
||||
get_representation_path,
|
||||
)
|
||||
from openpype.lib.transcoding import (
|
||||
VIDEO_EXTENSIONS,
|
||||
IMAGE_EXTENSIONS
|
||||
)
|
||||
import openpype.hosts.hiero.api as phiero
|
||||
|
||||
|
||||
|
|
@ -17,7 +21,10 @@ class LoadClip(phiero.SequenceLoader):
|
|||
"""
|
||||
|
||||
families = ["render2d", "source", "plate", "render", "review"]
|
||||
representations = ["exr", "dpx", "jpg", "jpeg", "png", "h264"]
|
||||
representations = ["*"]
|
||||
extensions = set(
|
||||
ext.lstrip(".") for ext in IMAGE_EXTENSIONS.union(VIDEO_EXTENSIONS)
|
||||
)
|
||||
|
||||
label = "Load as clip"
|
||||
order = -10
|
||||
|
|
@ -34,6 +41,38 @@ class LoadClip(phiero.SequenceLoader):
|
|||
|
||||
clip_name_template = "{asset}_{subset}_{representation}"
|
||||
|
||||
def apply_settings(cls, project_settings, system_settings):
|
||||
|
||||
plugin_type_settings = (
|
||||
project_settings
|
||||
.get("hiero", {})
|
||||
.get("load", {})
|
||||
)
|
||||
|
||||
if not plugin_type_settings:
|
||||
return
|
||||
|
||||
plugin_name = cls.__name__
|
||||
|
||||
plugin_settings = None
|
||||
# Look for plugin settings in host specific settings
|
||||
if plugin_name in plugin_type_settings:
|
||||
plugin_settings = plugin_type_settings[plugin_name]
|
||||
|
||||
if not plugin_settings:
|
||||
return
|
||||
|
||||
print(">>> We have preset for {}".format(plugin_name))
|
||||
for option, value in plugin_settings.items():
|
||||
if option == "enabled" and value is False:
|
||||
print(" - is disabled by preset")
|
||||
elif option == "representations":
|
||||
continue
|
||||
else:
|
||||
print(" - setting `{}`: `{}`".format(option, value))
|
||||
setattr(cls, option, value)
|
||||
|
||||
|
||||
def load(self, context, name, namespace, options):
|
||||
# add clip name template to options
|
||||
options.update({
|
||||
|
|
|
|||
|
|
@ -19,8 +19,9 @@ from openpype.lib import Logger
|
|||
class LoadEffects(load.LoaderPlugin):
|
||||
"""Loading colorspace soft effect exported from nukestudio"""
|
||||
|
||||
representations = ["effectJson"]
|
||||
families = ["effect"]
|
||||
representations = ["*"]
|
||||
extension = {"json"}
|
||||
|
||||
label = "Load Effects"
|
||||
order = 0
|
||||
|
|
|
|||
|
|
@ -120,13 +120,9 @@ class CollectClipEffects(pyblish.api.InstancePlugin):
|
|||
track = sitem.parentTrack().name()
|
||||
# node serialization
|
||||
node = sitem.node()
|
||||
node_serialized = self.node_serialisation(node)
|
||||
node_serialized = self.node_serialization(node)
|
||||
node_name = sitem.name()
|
||||
|
||||
if "_" in node_name:
|
||||
node_class = re.sub(r"(?:_)[_0-9]+", "", node_name) # more numbers
|
||||
else:
|
||||
node_class = re.sub(r"\d+", "", node_name) # one number
|
||||
node_class = node.Class()
|
||||
|
||||
# collect timelineIn/Out
|
||||
effect_t_in = int(sitem.timelineIn())
|
||||
|
|
@ -148,7 +144,7 @@ class CollectClipEffects(pyblish.api.InstancePlugin):
|
|||
"node": node_serialized
|
||||
}}
|
||||
|
||||
def node_serialisation(self, node):
|
||||
def node_serialization(self, node):
|
||||
node_serialized = {}
|
||||
|
||||
# adding ignoring knob keys
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import os
|
|||
import sys
|
||||
import platform
|
||||
import uuid
|
||||
import math
|
||||
import re
|
||||
|
||||
import json
|
||||
|
|
@ -403,9 +402,9 @@ def lsattrs(attrs):
|
|||
|
||||
"""
|
||||
|
||||
dep_fn = om.MFnDependencyNode()
|
||||
dag_fn = om.MFnDagNode()
|
||||
selection_list = om.MSelectionList()
|
||||
dep_fn = OpenMaya.MFnDependencyNode()
|
||||
dag_fn = OpenMaya.MFnDagNode()
|
||||
selection_list = OpenMaya.MSelectionList()
|
||||
|
||||
first_attr = next(iter(attrs))
|
||||
|
||||
|
|
@ -419,7 +418,7 @@ def lsattrs(attrs):
|
|||
matches = set()
|
||||
for i in range(selection_list.length()):
|
||||
node = selection_list.getDependNode(i)
|
||||
if node.hasFn(om.MFn.kDagNode):
|
||||
if node.hasFn(OpenMaya.MFn.kDagNode):
|
||||
fn_node = dag_fn.setObject(node)
|
||||
full_path_names = [path.fullPathName()
|
||||
for path in fn_node.getAllPaths()]
|
||||
|
|
@ -868,11 +867,11 @@ def maintained_selection_api():
|
|||
Warning: This is *not* added to the undo stack.
|
||||
|
||||
"""
|
||||
original = om.MGlobal.getActiveSelectionList()
|
||||
original = OpenMaya.MGlobal.getActiveSelectionList()
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
om.MGlobal.setActiveSelectionList(original)
|
||||
OpenMaya.MGlobal.setActiveSelectionList(original)
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
|
|
@ -1282,11 +1281,11 @@ def get_id(node):
|
|||
if node is None:
|
||||
return
|
||||
|
||||
sel = om.MSelectionList()
|
||||
sel = OpenMaya.MSelectionList()
|
||||
sel.add(node)
|
||||
|
||||
api_node = sel.getDependNode(0)
|
||||
fn = om.MFnDependencyNode(api_node)
|
||||
fn = OpenMaya.MFnDependencyNode(api_node)
|
||||
|
||||
if not fn.hasAttribute("cbId"):
|
||||
return
|
||||
|
|
@ -2064,13 +2063,8 @@ def set_scene_resolution(width, height, pixelAspect):
|
|||
cmds.setAttr("%s.pixelAspect" % control_node, pixelAspect)
|
||||
|
||||
|
||||
def reset_frame_range():
|
||||
"""Set frame range to current asset"""
|
||||
|
||||
fps = convert_to_maya_fps(
|
||||
float(legacy_io.Session.get("AVALON_FPS", 25))
|
||||
)
|
||||
set_scene_fps(fps)
|
||||
def get_frame_range():
|
||||
"""Get the current assets frame range and handles."""
|
||||
|
||||
# Set frame start/end
|
||||
project_name = legacy_io.active_project()
|
||||
|
|
@ -2097,8 +2091,26 @@ def reset_frame_range():
|
|||
if handle_end is None:
|
||||
handle_end = handles
|
||||
|
||||
frame_start -= int(handle_start)
|
||||
frame_end += int(handle_end)
|
||||
return {
|
||||
"frameStart": frame_start,
|
||||
"frameEnd": frame_end,
|
||||
"handleStart": handle_start,
|
||||
"handleEnd": handle_end
|
||||
}
|
||||
|
||||
|
||||
def reset_frame_range():
|
||||
"""Set frame range to current asset"""
|
||||
|
||||
fps = convert_to_maya_fps(
|
||||
float(legacy_io.Session.get("AVALON_FPS", 25))
|
||||
)
|
||||
set_scene_fps(fps)
|
||||
|
||||
frame_range = get_frame_range()
|
||||
|
||||
frame_start = frame_range["frameStart"] - int(frame_range["handleStart"])
|
||||
frame_end = frame_range["frameEnd"] + int(frame_range["handleEnd"])
|
||||
|
||||
cmds.playbackOptions(minTime=frame_start)
|
||||
cmds.playbackOptions(maxTime=frame_end)
|
||||
|
|
@ -3341,15 +3353,15 @@ def iter_visible_nodes_in_range(nodes, start, end):
|
|||
@memodict
|
||||
def get_visibility_mplug(node):
|
||||
"""Return api 2.0 MPlug with cached memoize decorator"""
|
||||
sel = om.MSelectionList()
|
||||
sel = OpenMaya.MSelectionList()
|
||||
sel.add(node)
|
||||
dag = sel.getDagPath(0)
|
||||
return om.MFnDagNode(dag).findPlug("visibility", True)
|
||||
return OpenMaya.MFnDagNode(dag).findPlug("visibility", True)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def dgcontext(mtime):
|
||||
"""MDGContext context manager"""
|
||||
context = om.MDGContext(mtime)
|
||||
context = OpenMaya.MDGContext(mtime)
|
||||
try:
|
||||
previous = context.makeCurrent()
|
||||
yield context
|
||||
|
|
@ -3358,9 +3370,9 @@ def iter_visible_nodes_in_range(nodes, start, end):
|
|||
|
||||
# We skip the first frame as we already used that frame to check for
|
||||
# overall visibilities. And end+1 to include the end frame.
|
||||
scene_units = om.MTime.uiUnit()
|
||||
scene_units = OpenMaya.MTime.uiUnit()
|
||||
for frame in range(start + 1, end + 1):
|
||||
mtime = om.MTime(frame, unit=scene_units)
|
||||
mtime = OpenMaya.MTime(frame, unit=scene_units)
|
||||
|
||||
# Build little cache so we don't query the same MPlug's value
|
||||
# again if it was checked on this frame and also is a dependency
|
||||
|
|
@ -3621,3 +3633,34 @@ def guess_colorspace(img_info):
|
|||
print(("[maketx] Error: Could not guess"
|
||||
"colorspace for {}").format(img_info["filename"]))
|
||||
return "linear"
|
||||
|
||||
|
||||
def len_flattened(components):
|
||||
"""Return the length of the list as if it was flattened.
|
||||
|
||||
Maya will return consecutive components as a single entry
|
||||
when requesting with `maya.cmds.ls` without the `flatten`
|
||||
flag. Though enabling `flatten` on a large list (e.g. millions)
|
||||
will result in a slow result. This command will return the amount
|
||||
of entries in a non-flattened list by parsing the result with
|
||||
regex.
|
||||
|
||||
Args:
|
||||
components (list): The non-flattened components.
|
||||
|
||||
Returns:
|
||||
int: The amount of entries.
|
||||
|
||||
"""
|
||||
assert isinstance(components, (list, tuple))
|
||||
n = 0
|
||||
|
||||
pattern = re.compile(r"\[(\d+):(\d+)\]")
|
||||
for c in components:
|
||||
match = pattern.search(c)
|
||||
if match:
|
||||
start, end = match.groups()
|
||||
n += int(end) - int(start) + 1
|
||||
else:
|
||||
n += 1
|
||||
return n
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ class CreateAnimation(plugin.Creator):
|
|||
icon = "male"
|
||||
write_color_sets = False
|
||||
write_face_sets = False
|
||||
include_user_defined_attributes = False
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CreateAnimation, self).__init__(*args, **kwargs)
|
||||
|
|
@ -47,3 +48,6 @@ class CreateAnimation(plugin.Creator):
|
|||
|
||||
# Default to write normals.
|
||||
self.data["writeNormals"] = True
|
||||
|
||||
value = self.include_user_defined_attributes
|
||||
self.data["includeUserDefinedAttributes"] = value
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ class CreatePointCache(plugin.Creator):
|
|||
icon = "gears"
|
||||
write_color_sets = False
|
||||
write_face_sets = False
|
||||
include_user_defined_attributes = False
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CreatePointCache, self).__init__(*args, **kwargs)
|
||||
|
|
@ -33,6 +34,8 @@ class CreatePointCache(plugin.Creator):
|
|||
self.data["refresh"] = False # Default to suspend refresh.
|
||||
|
||||
# Add options for custom attributes
|
||||
value = self.include_user_defined_attributes
|
||||
self.data["includeUserDefinedAttributes"] = value
|
||||
self.data["attr"] = ""
|
||||
self.data["attrPrefix"] = ""
|
||||
|
||||
|
|
|
|||
|
|
@ -25,16 +25,20 @@ class CreateReview(plugin.Creator):
|
|||
"depth peeling",
|
||||
"alpha cut"
|
||||
]
|
||||
useMayaTimeline = True
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CreateReview, self).__init__(*args, **kwargs)
|
||||
|
||||
# get basic animation data : start / end / handles / steps
|
||||
data = OrderedDict(**self.data)
|
||||
animation_data = lib.collect_animation_data(fps=True)
|
||||
for key, value in animation_data.items():
|
||||
|
||||
# Option for using Maya or asset frame range in settings.
|
||||
frame_range = lib.get_frame_range()
|
||||
if self.useMayaTimeline:
|
||||
frame_range = lib.collect_animation_data(fps=True)
|
||||
for key, value in frame_range.items():
|
||||
data[key] = value
|
||||
|
||||
data["fps"] = lib.collect_animation_data(fps=True)["fps"]
|
||||
data["review_width"] = self.Width
|
||||
data["review_height"] = self.Height
|
||||
data["isolate"] = self.isolate
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@ class CreateVrayProxy(plugin.Creator):
|
|||
family = "vrayproxy"
|
||||
icon = "gears"
|
||||
|
||||
vrmesh = True
|
||||
alembic = True
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CreateVrayProxy, self).__init__(*args, **kwargs)
|
||||
|
||||
|
|
@ -18,3 +21,6 @@ class CreateVrayProxy(plugin.Creator):
|
|||
|
||||
# Write vertex colors
|
||||
self.data["vertexColors"] = False
|
||||
|
||||
self.data["vrmesh"] = self.vrmesh
|
||||
self.data["alembic"] = self.alembic
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@ class CollectAnimationOutputGeometry(pyblish.api.InstancePlugin):
|
|||
|
||||
hierarchy = members + descendants
|
||||
|
||||
|
||||
# Ignore certain node types (e.g. constraints)
|
||||
ignore = cmds.ls(hierarchy, type=self.ignore_type, long=True)
|
||||
if ignore:
|
||||
|
|
@ -58,3 +57,18 @@ class CollectAnimationOutputGeometry(pyblish.api.InstancePlugin):
|
|||
|
||||
if instance.data.get("farm"):
|
||||
instance.data["families"].append("publish.farm")
|
||||
|
||||
# Collect user defined attributes.
|
||||
if not instance.data.get("includeUserDefinedAttributes", False):
|
||||
return
|
||||
|
||||
user_defined_attributes = set()
|
||||
for node in hierarchy:
|
||||
attrs = cmds.listAttr(node, userDefined=True) or list()
|
||||
shapes = cmds.listRelatives(node, shapes=True) or list()
|
||||
for shape in shapes:
|
||||
attrs.extend(cmds.listAttr(shape, userDefined=True) or list())
|
||||
|
||||
user_defined_attributes.update(attrs)
|
||||
|
||||
instance.data["userDefinedAttributes"] = list(user_defined_attributes)
|
||||
|
|
|
|||
|
|
@ -137,6 +137,7 @@ class CollectInstances(pyblish.api.ContextPlugin):
|
|||
# Create the instance
|
||||
instance = context.create_instance(objset)
|
||||
instance[:] = members_hierarchy
|
||||
instance.data["objset"] = objset
|
||||
|
||||
# Store the exact members of the object set
|
||||
instance.data["setMembers"] = members
|
||||
|
|
|
|||
|
|
@ -42,3 +42,18 @@ class CollectPointcache(pyblish.api.InstancePlugin):
|
|||
if proxy_set:
|
||||
instance.remove(proxy_set)
|
||||
instance.data["setMembers"].remove(proxy_set)
|
||||
|
||||
# Collect user defined attributes.
|
||||
if not instance.data.get("includeUserDefinedAttributes", False):
|
||||
return
|
||||
|
||||
user_defined_attributes = set()
|
||||
for node in instance:
|
||||
attrs = cmds.listAttr(node, userDefined=True) or list()
|
||||
shapes = cmds.listRelatives(node, shapes=True) or list()
|
||||
for shape in shapes:
|
||||
attrs.extend(cmds.listAttr(shape, userDefined=True) or list())
|
||||
|
||||
user_defined_attributes.update(attrs)
|
||||
|
||||
instance.data["userDefinedAttributes"] = list(user_defined_attributes)
|
||||
|
|
|
|||
|
|
@ -9,10 +9,16 @@ class CollectVrayProxy(pyblish.api.InstancePlugin):
|
|||
Add `pointcache` family for it.
|
||||
"""
|
||||
order = pyblish.api.CollectorOrder + 0.01
|
||||
label = 'Collect Vray Proxy'
|
||||
label = "Collect Vray Proxy"
|
||||
families = ["vrayproxy"]
|
||||
|
||||
def process(self, instance):
|
||||
"""Collector entry point."""
|
||||
if not instance.data.get('families'):
|
||||
instance.data["families"] = []
|
||||
|
||||
if instance.data.get("vrmesh"):
|
||||
instance.data["families"].append("vrayproxy.vrmesh")
|
||||
|
||||
if instance.data.get("alembic"):
|
||||
instance.data["families"].append("vrayproxy.alembic")
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import os
|
||||
import copy
|
||||
|
||||
from maya import cmds
|
||||
|
||||
|
|
@ -10,7 +9,6 @@ from openpype.hosts.maya.api.lib import (
|
|||
maintained_selection,
|
||||
iter_visible_nodes_in_range
|
||||
)
|
||||
from openpype.lib import StringTemplate
|
||||
|
||||
|
||||
class ExtractAlembic(publish.Extractor):
|
||||
|
|
@ -25,7 +23,7 @@ class ExtractAlembic(publish.Extractor):
|
|||
|
||||
label = "Extract Pointcache (Alembic)"
|
||||
hosts = ["maya"]
|
||||
families = ["pointcache", "model", "vrayproxy"]
|
||||
families = ["pointcache", "model", "vrayproxy.alembic"]
|
||||
targets = ["local", "remote"]
|
||||
|
||||
def process(self, instance):
|
||||
|
|
@ -41,6 +39,7 @@ class ExtractAlembic(publish.Extractor):
|
|||
|
||||
attrs = instance.data.get("attr", "").split(";")
|
||||
attrs = [value for value in attrs if value.strip()]
|
||||
attrs += instance.data.get("userDefinedAttributes", [])
|
||||
attrs += ["cbId"]
|
||||
|
||||
attr_prefixes = instance.data.get("attrPrefix", "").split(";")
|
||||
|
|
@ -115,6 +114,7 @@ class ExtractAlembic(publish.Extractor):
|
|||
|
||||
# Extract proxy.
|
||||
if not instance.data.get("proxy"):
|
||||
self.log.info("No proxy nodes found. Skipping proxy extraction.")
|
||||
return
|
||||
|
||||
path = path.replace(".abc", "_proxy.abc")
|
||||
|
|
@ -134,26 +134,14 @@ class ExtractAlembic(publish.Extractor):
|
|||
**options
|
||||
)
|
||||
|
||||
template_data = copy.deepcopy(instance.data["anatomyData"])
|
||||
template_data.update({"ext": "abc"})
|
||||
templates = instance.context.data["anatomy"].templates["publish"]
|
||||
published_filename_without_extension = StringTemplate(
|
||||
templates["file"]
|
||||
).format(template_data).replace(".abc", "_proxy")
|
||||
transfers = []
|
||||
destination = os.path.join(
|
||||
instance.data["resourcesDir"],
|
||||
filename.replace(
|
||||
filename.split(".")[0],
|
||||
published_filename_without_extension
|
||||
)
|
||||
)
|
||||
transfers.append((path, destination))
|
||||
|
||||
for source, destination in transfers:
|
||||
self.log.debug("Transfer: {} > {}".format(source, destination))
|
||||
|
||||
instance.data["transfers"] = transfers
|
||||
representation = {
|
||||
"name": "proxy",
|
||||
"ext": "abc",
|
||||
"files": os.path.basename(path),
|
||||
"stagingDir": dirname,
|
||||
"outputName": "proxy"
|
||||
}
|
||||
instance.data["representations"].append(representation)
|
||||
|
||||
def get_members_and_roots(self, instance):
|
||||
return instance[:], instance.data.get("setMembers")
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class ExtractVRayProxy(publish.Extractor):
|
|||
|
||||
label = "VRay Proxy (.vrmesh)"
|
||||
hosts = ["maya"]
|
||||
families = ["vrayproxy"]
|
||||
families = ["vrayproxy.vrmesh"]
|
||||
|
||||
def process(self, instance):
|
||||
|
||||
|
|
|
|||
|
|
@ -57,6 +57,10 @@ class ValidateFrameRange(pyblish.api.InstancePlugin):
|
|||
|
||||
inst_start = int(instance.data.get("frameStartHandle"))
|
||||
inst_end = int(instance.data.get("frameEndHandle"))
|
||||
inst_frame_start = int(instance.data.get("frameStart"))
|
||||
inst_frame_end = int(instance.data.get("frameEnd"))
|
||||
inst_handle_start = int(instance.data.get("handleStart"))
|
||||
inst_handle_end = int(instance.data.get("handleEnd"))
|
||||
|
||||
# basic sanity checks
|
||||
assert frame_start_handle <= frame_end_handle, (
|
||||
|
|
@ -69,24 +73,37 @@ class ValidateFrameRange(pyblish.api.InstancePlugin):
|
|||
if [ef for ef in self.exclude_families
|
||||
if instance.data["family"] in ef]:
|
||||
return
|
||||
if(inst_start != frame_start_handle):
|
||||
if (inst_start != frame_start_handle):
|
||||
errors.append("Instance start frame [ {} ] doesn't "
|
||||
"match the one set on instance [ {} ]: "
|
||||
"match the one set on asset [ {} ]: "
|
||||
"{}/{}/{}/{} (handle/start/end/handle)".format(
|
||||
inst_start,
|
||||
frame_start_handle,
|
||||
handle_start, frame_start, frame_end, handle_end
|
||||
))
|
||||
|
||||
if(inst_end != frame_end_handle):
|
||||
if (inst_end != frame_end_handle):
|
||||
errors.append("Instance end frame [ {} ] doesn't "
|
||||
"match the one set on instance [ {} ]: "
|
||||
"match the one set on asset [ {} ]: "
|
||||
"{}/{}/{}/{} (handle/start/end/handle)".format(
|
||||
inst_end,
|
||||
frame_end_handle,
|
||||
handle_start, frame_start, frame_end, handle_end
|
||||
))
|
||||
|
||||
checks = {
|
||||
"frame start": (frame_start, inst_frame_start),
|
||||
"frame end": (frame_end, inst_frame_end),
|
||||
"handle start": (handle_start, inst_handle_start),
|
||||
"handle end": (handle_end, inst_handle_end)
|
||||
}
|
||||
for label, values in checks.items():
|
||||
if values[0] != values[1]:
|
||||
errors.append(
|
||||
"{} on instance ({}) does not match with the asset "
|
||||
"({}).".format(label.title(), values[1], values[0])
|
||||
)
|
||||
|
||||
for e in errors:
|
||||
self.log.error(e)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
from maya import cmds
|
||||
|
||||
import pyblish.api
|
||||
from openpype.pipeline.publish import (
|
||||
ValidateContentsOrder, PublishValidationError, RepairAction
|
||||
)
|
||||
from openpype.pipeline import discover_legacy_creator_plugins
|
||||
from openpype.hosts.maya.api.lib import imprint
|
||||
|
||||
|
||||
class ValidateInstanceAttributes(pyblish.api.InstancePlugin):
|
||||
"""Validate Instance Attributes.
|
||||
|
||||
New attributes can be introduced as new features come in. Old instances
|
||||
will need to be updated with these attributes for the documentation to make
|
||||
sense, and users do not have to recreate the instances.
|
||||
"""
|
||||
|
||||
order = ValidateContentsOrder
|
||||
hosts = ["maya"]
|
||||
families = ["*"]
|
||||
label = "Instance Attributes"
|
||||
plugins_by_family = {
|
||||
p.family: p for p in discover_legacy_creator_plugins()
|
||||
}
|
||||
actions = [RepairAction]
|
||||
|
||||
@classmethod
|
||||
def get_missing_attributes(self, instance):
|
||||
plugin = self.plugins_by_family[instance.data["family"]]
|
||||
subset = instance.data["subset"]
|
||||
asset = instance.data["asset"]
|
||||
objset = instance.data["objset"]
|
||||
|
||||
missing_attributes = {}
|
||||
for key, value in plugin(subset, asset).data.items():
|
||||
if not cmds.objExists("{}.{}".format(objset, key)):
|
||||
missing_attributes[key] = value
|
||||
|
||||
return missing_attributes
|
||||
|
||||
def process(self, instance):
|
||||
objset = instance.data.get("objset")
|
||||
if objset is None:
|
||||
self.log.debug(
|
||||
"Skipping {} because no objectset found.".format(instance)
|
||||
)
|
||||
return
|
||||
|
||||
missing_attributes = self.get_missing_attributes(instance)
|
||||
if missing_attributes:
|
||||
raise PublishValidationError(
|
||||
"Missing attributes on {}:\n{}".format(
|
||||
objset, missing_attributes
|
||||
)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def repair(cls, instance):
|
||||
imprint(instance.data["objset"], cls.get_missing_attributes(instance))
|
||||
54
openpype/hosts/maya/plugins/publish/validate_mesh_empty.py
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
from maya import cmds
|
||||
|
||||
import pyblish.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.pipeline.publish import (
|
||||
RepairAction,
|
||||
ValidateMeshOrder
|
||||
)
|
||||
|
||||
|
||||
class ValidateMeshEmpty(pyblish.api.InstancePlugin):
|
||||
"""Validate meshes have some vertices.
|
||||
|
||||
Its possible to have meshes without any vertices. To replicate
|
||||
this issue, delete all faces/polygons then all edges.
|
||||
"""
|
||||
|
||||
order = ValidateMeshOrder
|
||||
hosts = ["maya"]
|
||||
families = ["model"]
|
||||
label = "Mesh Empty"
|
||||
actions = [
|
||||
openpype.hosts.maya.api.action.SelectInvalidAction, RepairAction
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def repair(cls, instance):
|
||||
invalid = cls.get_invalid(instance)
|
||||
for node in invalid:
|
||||
cmds.delete(node)
|
||||
|
||||
@classmethod
|
||||
def get_invalid(cls, instance):
|
||||
invalid = []
|
||||
|
||||
meshes = cmds.ls(instance, type="mesh", long=True)
|
||||
for mesh in meshes:
|
||||
num_vertices = cmds.polyEvaluate(mesh, vertex=True)
|
||||
|
||||
if num_vertices == 0:
|
||||
cls.log.warning(
|
||||
"\"{}\" does not have any vertices.".format(mesh)
|
||||
)
|
||||
invalid.append(mesh)
|
||||
|
||||
return invalid
|
||||
|
||||
def process(self, instance):
|
||||
|
||||
invalid = self.get_invalid(instance)
|
||||
if invalid:
|
||||
raise RuntimeError(
|
||||
"Meshes found in instance without any vertices: %s" % invalid
|
||||
)
|
||||
|
|
@ -1,39 +1,9 @@
|
|||
import re
|
||||
|
||||
from maya import cmds
|
||||
|
||||
import pyblish.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.pipeline.publish import ValidateMeshOrder
|
||||
|
||||
|
||||
def len_flattened(components):
|
||||
"""Return the length of the list as if it was flattened.
|
||||
|
||||
Maya will return consecutive components as a single entry
|
||||
when requesting with `maya.cmds.ls` without the `flatten`
|
||||
flag. Though enabling `flatten` on a large list (e.g. millions)
|
||||
will result in a slow result. This command will return the amount
|
||||
of entries in a non-flattened list by parsing the result with
|
||||
regex.
|
||||
|
||||
Args:
|
||||
components (list): The non-flattened components.
|
||||
|
||||
Returns:
|
||||
int: The amount of entries.
|
||||
|
||||
"""
|
||||
assert isinstance(components, (list, tuple))
|
||||
n = 0
|
||||
for c in components:
|
||||
match = re.search("\[([0-9]+):([0-9]+)\]", c)
|
||||
if match:
|
||||
start, end = match.groups()
|
||||
n += int(end) - int(start) + 1
|
||||
else:
|
||||
n += 1
|
||||
return n
|
||||
from openpype.hosts.maya.api.lib import len_flattened
|
||||
|
||||
|
||||
class ValidateMeshHasUVs(pyblish.api.InstancePlugin):
|
||||
|
|
@ -57,6 +27,15 @@ class ValidateMeshHasUVs(pyblish.api.InstancePlugin):
|
|||
invalid = []
|
||||
|
||||
for node in cmds.ls(instance, type='mesh'):
|
||||
num_vertices = cmds.polyEvaluate(node, vertex=True)
|
||||
|
||||
if num_vertices == 0:
|
||||
cls.log.warning(
|
||||
"Skipping \"{}\", cause it does not have any "
|
||||
"vertices.".format(node)
|
||||
)
|
||||
continue
|
||||
|
||||
uv = cmds.polyEvaluate(node, uv=True)
|
||||
|
||||
if uv == 0:
|
||||
|
|
|
|||
|
|
@ -28,7 +28,10 @@ class ValidateMeshNonZeroEdgeLength(pyblish.api.InstancePlugin):
|
|||
@classmethod
|
||||
def get_invalid(cls, instance):
|
||||
"""Return the invalid edges.
|
||||
Also see: http://help.autodesk.com/view/MAYAUL/2015/ENU/?guid=Mesh__Cleanup
|
||||
|
||||
Also see:
|
||||
|
||||
http://help.autodesk.com/view/MAYAUL/2015/ENU/?guid=Mesh__Cleanup
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -36,8 +39,21 @@ class ValidateMeshNonZeroEdgeLength(pyblish.api.InstancePlugin):
|
|||
if not meshes:
|
||||
return list()
|
||||
|
||||
valid_meshes = []
|
||||
for mesh in meshes:
|
||||
num_vertices = cmds.polyEvaluate(mesh, vertex=True)
|
||||
|
||||
if num_vertices == 0:
|
||||
cls.log.warning(
|
||||
"Skipping \"{}\", cause it does not have any "
|
||||
"vertices.".format(mesh)
|
||||
)
|
||||
continue
|
||||
|
||||
valid_meshes.append(mesh)
|
||||
|
||||
# Get all edges
|
||||
edges = ['{0}.e[*]'.format(node) for node in meshes]
|
||||
edges = ['{0}.e[*]'.format(node) for node in valid_meshes]
|
||||
|
||||
# Filter by constraint on edge length
|
||||
invalid = lib.polyConstraint(edges,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
import re
|
||||
|
||||
from maya import cmds
|
||||
|
||||
import pyblish.api
|
||||
|
|
@ -8,37 +6,7 @@ from openpype.pipeline.publish import (
|
|||
RepairAction,
|
||||
ValidateMeshOrder,
|
||||
)
|
||||
|
||||
|
||||
def len_flattened(components):
|
||||
"""Return the length of the list as if it was flattened.
|
||||
|
||||
Maya will return consecutive components as a single entry
|
||||
when requesting with `maya.cmds.ls` without the `flatten`
|
||||
flag. Though enabling `flatten` on a large list (e.g. millions)
|
||||
will result in a slow result. This command will return the amount
|
||||
of entries in a non-flattened list by parsing the result with
|
||||
regex.
|
||||
|
||||
Args:
|
||||
components (list): The non-flattened components.
|
||||
|
||||
Returns:
|
||||
int: The amount of entries.
|
||||
|
||||
"""
|
||||
assert isinstance(components, (list, tuple))
|
||||
n = 0
|
||||
|
||||
pattern = re.compile(r"\[(\d+):(\d+)\]")
|
||||
for c in components:
|
||||
match = pattern.search(c)
|
||||
if match:
|
||||
start, end = match.groups()
|
||||
n += int(end) - int(start) + 1
|
||||
else:
|
||||
n += 1
|
||||
return n
|
||||
from openpype.hosts.maya.api.lib import len_flattened
|
||||
|
||||
|
||||
class ValidateMeshVerticesHaveEdges(pyblish.api.InstancePlugin):
|
||||
|
|
@ -87,6 +55,13 @@ class ValidateMeshVerticesHaveEdges(pyblish.api.InstancePlugin):
|
|||
for mesh in meshes:
|
||||
num_vertices = cmds.polyEvaluate(mesh, vertex=True)
|
||||
|
||||
if num_vertices == 0:
|
||||
cls.log.warning(
|
||||
"Skipping \"{}\", cause it does not have any "
|
||||
"vertices.".format(mesh)
|
||||
)
|
||||
continue
|
||||
|
||||
# Vertices from all edges
|
||||
edges = "%s.e[*]" % mesh
|
||||
vertices = cmds.polyListComponentConversion(edges, toVertex=True)
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@
|
|||
"""Validate model nodes names."""
|
||||
import os
|
||||
import re
|
||||
from maya import cmds
|
||||
import pyblish.api
|
||||
import platform
|
||||
|
||||
from maya import cmds
|
||||
|
||||
import pyblish.api
|
||||
from openpype.pipeline import legacy_io
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
import openpype.hosts.maya.api.action
|
||||
|
|
@ -44,7 +46,7 @@ class ValidateModelName(pyblish.api.InstancePlugin):
|
|||
if not cmds.ls(child, transforms=True):
|
||||
return False
|
||||
return True
|
||||
except:
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
invalid = []
|
||||
|
|
@ -94,9 +96,10 @@ class ValidateModelName(pyblish.api.InstancePlugin):
|
|||
# load shader list file as utf-8
|
||||
shaders = []
|
||||
if not use_db:
|
||||
if cls.material_file:
|
||||
if os.path.isfile(cls.material_file):
|
||||
shader_file = open(cls.material_file, "r")
|
||||
material_file = cls.material_file[platform.system().lower()]
|
||||
if material_file:
|
||||
if os.path.isfile(material_file):
|
||||
shader_file = open(material_file, "r")
|
||||
shaders = shader_file.readlines()
|
||||
shader_file.close()
|
||||
else:
|
||||
|
|
@ -113,7 +116,7 @@ class ValidateModelName(pyblish.api.InstancePlugin):
|
|||
shader_file.close()
|
||||
|
||||
# strip line endings from list
|
||||
shaders = map(lambda s: s.rstrip(), shaders)
|
||||
shaders = [s.rstrip() for s in shaders if s.rstrip()]
|
||||
|
||||
# compile regex for testing names
|
||||
regex = cls.regex
|
||||
|
|
|
|||
|
|
@ -1,27 +1,31 @@
|
|||
import pyblish.api
|
||||
|
||||
from openpype.pipeline import KnownPublishError
|
||||
|
||||
|
||||
class ValidateVrayProxy(pyblish.api.InstancePlugin):
|
||||
|
||||
order = pyblish.api.ValidatorOrder
|
||||
label = 'VRay Proxy Settings'
|
||||
hosts = ['maya']
|
||||
families = ['studio.vrayproxy']
|
||||
label = "VRay Proxy Settings"
|
||||
hosts = ["maya"]
|
||||
families = ["vrayproxy"]
|
||||
|
||||
def process(self, instance):
|
||||
|
||||
invalid = self.get_invalid(instance)
|
||||
if invalid:
|
||||
raise RuntimeError("'%s' has invalid settings for VRay Proxy "
|
||||
"export!" % instance.name)
|
||||
|
||||
@classmethod
|
||||
def get_invalid(cls, instance):
|
||||
data = instance.data
|
||||
|
||||
if not data["setMembers"]:
|
||||
cls.log.error("'%s' is empty! This is a bug" % instance.name)
|
||||
raise KnownPublishError(
|
||||
"'%s' is empty! This is a bug" % instance.name
|
||||
)
|
||||
|
||||
if data["animation"]:
|
||||
if data["frameEnd"] < data["frameStart"]:
|
||||
cls.log.error("End frame is smaller than start frame")
|
||||
raise KnownPublishError(
|
||||
"End frame is smaller than start frame"
|
||||
)
|
||||
|
||||
if not data["vrmesh"] and not data["alembic"]:
|
||||
raise KnownPublishError(
|
||||
"Both vrmesh and alembic are off. Needs at least one to"
|
||||
" publish."
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1264,7 +1264,7 @@ def convert_to_valid_instaces():
|
|||
creator_attr["farm_chunk"] = (
|
||||
node["deadlineChunkSize"].value())
|
||||
if "deadlineConcurrentTasks" in node.knobs():
|
||||
creator_attr["farm_concurency"] = (
|
||||
creator_attr["farm_concurrency"] = (
|
||||
node["deadlineConcurrentTasks"].value())
|
||||
|
||||
_remove_old_knobs(node)
|
||||
|
|
|
|||
|
|
@ -6,10 +6,7 @@ from openpype.pipeline import (
|
|||
CreatedInstance
|
||||
)
|
||||
from openpype.lib import (
|
||||
BoolDef,
|
||||
NumberDef,
|
||||
UISeparatorDef,
|
||||
UILabelDef
|
||||
BoolDef
|
||||
)
|
||||
from openpype.hosts.nuke import api as napi
|
||||
|
||||
|
|
@ -49,33 +46,6 @@ class CreateWritePrerender(napi.NukeWriteCreator):
|
|||
self._get_render_target_enum(),
|
||||
self._get_reviewable_bool()
|
||||
]
|
||||
if "farm_rendering" in self.instance_attributes:
|
||||
attr_defs.extend([
|
||||
UISeparatorDef(),
|
||||
UILabelDef("Farm rendering attributes"),
|
||||
BoolDef("suspended_publish", label="Suspended publishing"),
|
||||
NumberDef(
|
||||
"farm_priority",
|
||||
label="Priority",
|
||||
minimum=1,
|
||||
maximum=99,
|
||||
default=50
|
||||
),
|
||||
NumberDef(
|
||||
"farm_chunk",
|
||||
label="Chunk size",
|
||||
minimum=1,
|
||||
maximum=99,
|
||||
default=10
|
||||
),
|
||||
NumberDef(
|
||||
"farm_concurency",
|
||||
label="Concurent tasks",
|
||||
minimum=1,
|
||||
maximum=10,
|
||||
default=1
|
||||
)
|
||||
])
|
||||
return attr_defs
|
||||
|
||||
def create_instance_node(self, subset_name, instance_data):
|
||||
|
|
|
|||
|
|
@ -6,10 +6,7 @@ from openpype.pipeline import (
|
|||
CreatedInstance
|
||||
)
|
||||
from openpype.lib import (
|
||||
BoolDef,
|
||||
NumberDef,
|
||||
UISeparatorDef,
|
||||
UILabelDef
|
||||
BoolDef
|
||||
)
|
||||
from openpype.hosts.nuke import api as napi
|
||||
|
||||
|
|
@ -46,33 +43,6 @@ class CreateWriteRender(napi.NukeWriteCreator):
|
|||
self._get_render_target_enum(),
|
||||
self._get_reviewable_bool()
|
||||
]
|
||||
if "farm_rendering" in self.instance_attributes:
|
||||
attr_defs.extend([
|
||||
UISeparatorDef(),
|
||||
UILabelDef("Farm rendering attributes"),
|
||||
BoolDef("suspended_publish", label="Suspended publishing"),
|
||||
NumberDef(
|
||||
"farm_priority",
|
||||
label="Priority",
|
||||
minimum=1,
|
||||
maximum=99,
|
||||
default=50
|
||||
),
|
||||
NumberDef(
|
||||
"farm_chunk",
|
||||
label="Chunk size",
|
||||
minimum=1,
|
||||
maximum=99,
|
||||
default=10
|
||||
),
|
||||
NumberDef(
|
||||
"farm_concurency",
|
||||
label="Concurent tasks",
|
||||
minimum=1,
|
||||
maximum=10,
|
||||
default=1
|
||||
)
|
||||
])
|
||||
return attr_defs
|
||||
|
||||
def create_instance_node(self, subset_name, instance_data):
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ class SetFrameRangeLoader(load.LoaderPlugin):
|
|||
"yeticache",
|
||||
"pointcache"]
|
||||
representations = ["*"]
|
||||
extension = {"*"}
|
||||
|
||||
label = "Set frame range"
|
||||
order = 11
|
||||
|
|
|
|||
|
|
@ -25,8 +25,9 @@ from openpype.hosts.nuke.api import containerise, update_container
|
|||
class LoadBackdropNodes(load.LoaderPlugin):
|
||||
"""Loading Published Backdrop nodes (workfile, nukenodes)"""
|
||||
|
||||
representations = ["nk"]
|
||||
families = ["workfile", "nukenodes"]
|
||||
representations = ["*"]
|
||||
extension = {"nk"}
|
||||
|
||||
label = "Import Nuke Nodes"
|
||||
order = 0
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ class AlembicCameraLoader(load.LoaderPlugin):
|
|||
"""
|
||||
|
||||
families = ["camera"]
|
||||
representations = ["abc"]
|
||||
representations = ["*"]
|
||||
extension = {"abc"}
|
||||
|
||||
label = "Load Alembic Camera"
|
||||
icon = "camera"
|
||||
|
|
|
|||
|
|
@ -21,6 +21,10 @@ from openpype.hosts.nuke.api import (
|
|||
viewer_update_and_undo_stop,
|
||||
colorspace_exists_on_node
|
||||
)
|
||||
from openpype.lib.transcoding import (
|
||||
VIDEO_EXTENSIONS,
|
||||
IMAGE_EXTENSIONS
|
||||
)
|
||||
from openpype.hosts.nuke.api import plugin
|
||||
|
||||
|
||||
|
|
@ -38,13 +42,10 @@ class LoadClip(plugin.NukeLoader):
|
|||
"prerender",
|
||||
"review"
|
||||
]
|
||||
representations = [
|
||||
"exr",
|
||||
"dpx",
|
||||
"mov",
|
||||
"review",
|
||||
"mp4"
|
||||
]
|
||||
representations = ["*"]
|
||||
extensions = set(
|
||||
ext.lstrip(".") for ext in IMAGE_EXTENSIONS.union(VIDEO_EXTENSIONS)
|
||||
)
|
||||
|
||||
label = "Load Clip"
|
||||
order = -20
|
||||
|
|
@ -81,17 +82,17 @@ class LoadClip(plugin.NukeLoader):
|
|||
|
||||
@classmethod
|
||||
def get_representations(cls):
|
||||
return (
|
||||
cls.representations
|
||||
+ cls._representations
|
||||
+ plugin.get_review_presets_config()
|
||||
)
|
||||
return cls._representations or cls.representations
|
||||
|
||||
def load(self, context, name, namespace, options):
|
||||
"""Load asset via database
|
||||
"""
|
||||
representation = context["representation"]
|
||||
# reste container id so it is always unique for each instance
|
||||
# reset container id so it is always unique for each instance
|
||||
self.reset_container_id()
|
||||
|
||||
self.log.warning(self.extensions)
|
||||
|
||||
is_sequence = len(representation["files"]) > 1
|
||||
|
||||
if is_sequence:
|
||||
|
|
|
|||
|
|
@ -22,8 +22,9 @@ from openpype.hosts.nuke.api import (
|
|||
class LoadEffects(load.LoaderPlugin):
|
||||
"""Loading colorspace soft effect exported from nukestudio"""
|
||||
|
||||
representations = ["effectJson"]
|
||||
families = ["effect"]
|
||||
representations = ["*"]
|
||||
extension = {"json"}
|
||||
|
||||
label = "Load Effects - nodes"
|
||||
order = 0
|
||||
|
|
|
|||
|
|
@ -23,8 +23,9 @@ from openpype.hosts.nuke.api import (
|
|||
class LoadEffectsInputProcess(load.LoaderPlugin):
|
||||
"""Loading colorspace soft effect exported from nukestudio"""
|
||||
|
||||
representations = ["effectJson"]
|
||||
families = ["effect"]
|
||||
representations = ["*"]
|
||||
extension = {"json"}
|
||||
|
||||
label = "Load Effects - Input Process"
|
||||
order = 0
|
||||
|
|
|
|||
|
|
@ -24,8 +24,9 @@ from openpype.hosts.nuke.api import (
|
|||
class LoadGizmo(load.LoaderPlugin):
|
||||
"""Loading nuke Gizmo"""
|
||||
|
||||
representations = ["gizmo"]
|
||||
families = ["gizmo"]
|
||||
representations = ["*"]
|
||||
extension = {"gizmo"}
|
||||
|
||||
label = "Load Gizmo"
|
||||
order = 0
|
||||
|
|
|
|||
|
|
@ -26,8 +26,9 @@ from openpype.hosts.nuke.api import (
|
|||
class LoadGizmoInputProcess(load.LoaderPlugin):
|
||||
"""Loading colorspace soft effect exported from nukestudio"""
|
||||
|
||||
representations = ["gizmo"]
|
||||
families = ["gizmo"]
|
||||
representations = ["*"]
|
||||
extension = {"gizmo"}
|
||||
|
||||
label = "Load Gizmo - Input Process"
|
||||
order = 0
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@ from openpype.hosts.nuke.api import (
|
|||
update_container,
|
||||
viewer_update_and_undo_stop
|
||||
)
|
||||
from openpype.lib.transcoding import (
|
||||
IMAGE_EXTENSIONS
|
||||
)
|
||||
|
||||
|
||||
class LoadImage(load.LoaderPlugin):
|
||||
|
|
@ -33,7 +36,10 @@ class LoadImage(load.LoaderPlugin):
|
|||
"review",
|
||||
"image"
|
||||
]
|
||||
representations = ["exr", "dpx", "jpg", "jpeg", "png", "psd", "tiff"]
|
||||
representations = ["*"]
|
||||
extensions = set(
|
||||
ext.lstrip(".") for ext in IMAGE_EXTENSIONS
|
||||
)
|
||||
|
||||
label = "Load Image"
|
||||
order = -10
|
||||
|
|
@ -58,7 +64,7 @@ class LoadImage(load.LoaderPlugin):
|
|||
|
||||
@classmethod
|
||||
def get_representations(cls):
|
||||
return cls.representations + cls._representations
|
||||
return cls._representations or cls.representations
|
||||
|
||||
def load(self, context, name, namespace, options):
|
||||
self.log.info("__ options: `{}`".format(options))
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@ class MatchmoveLoader(load.LoaderPlugin):
|
|||
"""
|
||||
|
||||
families = ["matchmove"]
|
||||
representations = ["py"]
|
||||
representations = ["*"]
|
||||
extension = {"py"}
|
||||
|
||||
defaults = ["Camera", "Object"]
|
||||
|
||||
label = "Run matchmove script"
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ class AlembicModelLoader(load.LoaderPlugin):
|
|||
"""
|
||||
|
||||
families = ["model", "pointcache", "animation"]
|
||||
representations = ["abc"]
|
||||
representations = ["*"]
|
||||
extension = {"abc"}
|
||||
|
||||
label = "Load Alembic"
|
||||
icon = "cube"
|
||||
|
|
|
|||
|
|
@ -20,8 +20,9 @@ from openpype.hosts.nuke.api import (
|
|||
class LinkAsGroup(load.LoaderPlugin):
|
||||
"""Copy the published file to be pasted at the desired location"""
|
||||
|
||||
representations = ["nk"]
|
||||
families = ["workfile", "nukenodes"]
|
||||
representations = ["*"]
|
||||
extension = {"nk"}
|
||||
|
||||
label = "Load Precomp"
|
||||
order = 0
|
||||
|
|
|
|||
|
|
@ -132,14 +132,14 @@ class CollectNukeWrites(pyblish.api.InstancePlugin):
|
|||
self.log.info("Publishing rendered frames ...")
|
||||
|
||||
elif render_target == "farm":
|
||||
farm_priority = creator_attributes.get("farm_priority")
|
||||
farm_chunk = creator_attributes.get("farm_chunk")
|
||||
farm_concurency = creator_attributes.get("farm_concurency")
|
||||
instance.data.update({
|
||||
"deadlineChunkSize": farm_chunk or 1,
|
||||
"deadlinePriority": farm_priority or 50,
|
||||
"deadlineConcurrentTasks": farm_concurency or 0
|
||||
})
|
||||
farm_keys = ["farm_chunk", "farm_priority", "farm_concurrency"]
|
||||
for key in farm_keys:
|
||||
# Skip if key is not in creator attributes
|
||||
if key not in creator_attributes:
|
||||
continue
|
||||
# Add farm attributes to instance
|
||||
instance.data[key] = creator_attributes[key]
|
||||
|
||||
# Farm rendering
|
||||
instance.data["transfer"] = False
|
||||
instance.data["farm"] = True
|
||||
|
|
|
|||
|
|
@ -14,7 +14,10 @@ from openpype.hosts.resolve.api.pipeline import (
|
|||
containerise,
|
||||
update_container,
|
||||
)
|
||||
|
||||
from openpype.lib.transcoding import (
|
||||
VIDEO_EXTENSIONS,
|
||||
IMAGE_EXTENSIONS
|
||||
)
|
||||
|
||||
class LoadClip(plugin.TimelineItemLoader):
|
||||
"""Load a subset to timeline as clip
|
||||
|
|
@ -24,7 +27,11 @@ class LoadClip(plugin.TimelineItemLoader):
|
|||
"""
|
||||
|
||||
families = ["render2d", "source", "plate", "render", "review"]
|
||||
representations = ["exr", "dpx", "jpg", "jpeg", "png", "h264", "mov"]
|
||||
|
||||
representations = ["*"]
|
||||
extensions = set(
|
||||
ext.lstrip(".") for ext in IMAGE_EXTENSIONS.union(VIDEO_EXTENSIONS)
|
||||
)
|
||||
|
||||
label = "Load as clip"
|
||||
order = -10
|
||||
|
|
|
|||
|
|
@ -17,9 +17,10 @@ class UnrealAddon(OpenPypeModule, IHostAddon):
|
|||
|
||||
ue_plugin = "UE_5.0" if app.name[:1] == "5" else "UE_4.7"
|
||||
unreal_plugin_path = os.path.join(
|
||||
UNREAL_ROOT_DIR, "integration", ue_plugin
|
||||
UNREAL_ROOT_DIR, "integration", ue_plugin, "OpenPype"
|
||||
)
|
||||
if not env.get("OPENPYPE_UNREAL_PLUGIN"):
|
||||
if not env.get("OPENPYPE_UNREAL_PLUGIN") or \
|
||||
env.get("OPENPYPE_UNREAL_PLUGIN") != unreal_plugin_path:
|
||||
env["OPENPYPE_UNREAL_PLUGIN"] = unreal_plugin_path
|
||||
|
||||
# Set default environments if are not set via settings
|
||||
|
|
|
|||
|
|
@ -79,9 +79,9 @@ class UnrealPrelaunchHook(PreLaunchHook):
|
|||
unreal_project_name = os.path.splitext(unreal_project_filename)[0]
|
||||
# Unreal is sensitive about project names longer then 20 chars
|
||||
if len(unreal_project_name) > 20:
|
||||
self.log.warning((
|
||||
f"Project name exceed 20 characters ({unreal_project_name})!"
|
||||
))
|
||||
raise ApplicationLaunchFailed(
|
||||
f"Project name exceeds 20 characters ({unreal_project_name})!"
|
||||
)
|
||||
|
||||
# Unreal doesn't accept non alphabet characters at the start
|
||||
# of the project name. This is because project name is then used
|
||||
|
|
@ -119,29 +119,34 @@ class UnrealPrelaunchHook(PreLaunchHook):
|
|||
f"detected [ {engine_version} ]"
|
||||
))
|
||||
|
||||
ue_path = unreal_lib.get_editor_executable_path(
|
||||
ue_path = unreal_lib.get_editor_exe_path(
|
||||
Path(detected[engine_version]), engine_version)
|
||||
|
||||
self.launch_context.launch_args = [ue_path.as_posix()]
|
||||
project_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Set "OPENPYPE_UNREAL_PLUGIN" to current process environment for
|
||||
# execution of `create_unreal_project`
|
||||
|
||||
if self.launch_context.env.get("OPENPYPE_UNREAL_PLUGIN"):
|
||||
self.log.info((
|
||||
f"{self.signature} using OpenPype plugin from "
|
||||
f"{self.launch_context.env.get('OPENPYPE_UNREAL_PLUGIN')}"
|
||||
))
|
||||
env_key = "OPENPYPE_UNREAL_PLUGIN"
|
||||
if self.launch_context.env.get(env_key):
|
||||
os.environ[env_key] = self.launch_context.env[env_key]
|
||||
|
||||
engine_path = detected[engine_version]
|
||||
|
||||
unreal_lib.try_installing_plugin(Path(engine_path), os.environ)
|
||||
|
||||
project_file = project_path / unreal_project_filename
|
||||
if not project_file.is_file():
|
||||
engine_path = detected[engine_version]
|
||||
self.log.info((
|
||||
f"{self.signature} creating unreal "
|
||||
f"project [ {unreal_project_name} ]"
|
||||
))
|
||||
# Set "OPENPYPE_UNREAL_PLUGIN" to current process environment for
|
||||
# execution of `create_unreal_project`
|
||||
if self.launch_context.env.get("OPENPYPE_UNREAL_PLUGIN"):
|
||||
self.log.info((
|
||||
f"{self.signature} using OpenPype plugin from "
|
||||
f"{self.launch_context.env.get('OPENPYPE_UNREAL_PLUGIN')}"
|
||||
))
|
||||
env_key = "OPENPYPE_UNREAL_PLUGIN"
|
||||
if self.launch_context.env.get(env_key):
|
||||
os.environ[env_key] = self.launch_context.env[env_key]
|
||||
|
||||
unreal_lib.create_unreal_project(
|
||||
unreal_project_name,
|
||||
|
|
|
|||
8
openpype/hosts/unreal/integration/UE_4.7/CommandletProject/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
/Saved
|
||||
/DerivedDataCache
|
||||
/Intermediate
|
||||
/Content
|
||||
/Config
|
||||
/Binaries
|
||||
/.idea
|
||||
/.vs
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"FileVersion": 3,
|
||||
"EngineAssociation": "4.27",
|
||||
"Category": "",
|
||||
"Description": "",
|
||||
"Plugins": [
|
||||
{
|
||||
"Name": "OpenPype",
|
||||
"Enabled": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
[FilterPlugin]
|
||||
; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and
|
||||
; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively.
|
||||
;
|
||||
; Examples:
|
||||
; /README.txt
|
||||
; /Extras/...
|
||||
; /Binaries/ThirdParty/*.dll
|
||||
|
|
@ -10,10 +10,9 @@
|
|||
"DocsURL": "https://openpype.io/docs/artist_hosts_unreal",
|
||||
"MarketplaceURL": "",
|
||||
"SupportURL": "https://pype.club/",
|
||||
"EngineVersion": "4.27",
|
||||
"CanContainContent": true,
|
||||
"IsBetaVersion": true,
|
||||
"IsExperimentalVersion": false,
|
||||
"Installed": false,
|
||||
"Installed": true,
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "OpenPype",
|
||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
|
||||
// Copyright 2023, Ayon, All rights reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
|
|
@ -6,8 +6,8 @@ public class OpenPype : ModuleRules
|
|||
{
|
||||
public OpenPype(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PublicIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add public include paths required here ...
|
||||
|
|
@ -34,6 +34,7 @@ public class OpenPype : ModuleRules
|
|||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"GameProjectGeneration",
|
||||
"Projects",
|
||||
"InputCore",
|
||||
"UnrealEd",
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
// Copyright 2023, Ayon, All rights reserved.
|
||||
#include "Commandlets/Implementations/OPGenerateProjectCommandlet.h"
|
||||
|
||||
#include "Editor.h"
|
||||
#include "GameProjectUtils.h"
|
||||
#include "OPConstants.h"
|
||||
#include "Commandlets/OPActionResult.h"
|
||||
#include "ProjectDescriptor.h"
|
||||
|
||||
int32 UOPGenerateProjectCommandlet::Main(const FString& CommandLineParams)
|
||||
{
|
||||
//Parses command line parameters & creates structure FProjectInformation
|
||||
const FOPGenerateProjectParams ParsedParams = FOPGenerateProjectParams(CommandLineParams);
|
||||
ProjectInformation = ParsedParams.GenerateUEProjectInformation();
|
||||
|
||||
//Creates .uproject & other UE files
|
||||
EVALUATE_OP_ACTION_RESULT(TryCreateProject());
|
||||
|
||||
//Loads created .uproject
|
||||
EVALUATE_OP_ACTION_RESULT(TryLoadProjectDescriptor());
|
||||
|
||||
//Adds needed plugin to .uproject
|
||||
AttachPluginsToProjectDescriptor();
|
||||
|
||||
//Saves .uproject
|
||||
EVALUATE_OP_ACTION_RESULT(TrySave());
|
||||
|
||||
//When we are here, there should not be problems in generating Unreal Project for OpenPype
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
FOPGenerateProjectParams::FOPGenerateProjectParams(): FOPGenerateProjectParams("")
|
||||
{
|
||||
}
|
||||
|
||||
FOPGenerateProjectParams::FOPGenerateProjectParams(const FString& CommandLineParams): CommandLineParams(
|
||||
CommandLineParams)
|
||||
{
|
||||
UCommandlet::ParseCommandLine(*CommandLineParams, Tokens, Switches);
|
||||
}
|
||||
|
||||
FProjectInformation FOPGenerateProjectParams::GenerateUEProjectInformation() const
|
||||
{
|
||||
FProjectInformation ProjectInformation = FProjectInformation();
|
||||
ProjectInformation.ProjectFilename = GetProjectFileName();
|
||||
|
||||
ProjectInformation.bShouldGenerateCode = IsSwitchPresent("GenerateCode");
|
||||
|
||||
return ProjectInformation;
|
||||
}
|
||||
|
||||
FString FOPGenerateProjectParams::TryGetToken(const int32 Index) const
|
||||
{
|
||||
return Tokens.IsValidIndex(Index) ? Tokens[Index] : "";
|
||||
}
|
||||
|
||||
FString FOPGenerateProjectParams::GetProjectFileName() const
|
||||
{
|
||||
return TryGetToken(0);
|
||||
}
|
||||
|
||||
bool FOPGenerateProjectParams::IsSwitchPresent(const FString& Switch) const
|
||||
{
|
||||
return INDEX_NONE != Switches.IndexOfByPredicate([&Switch](const FString& Item) -> bool
|
||||
{
|
||||
return Item.Equals(Switch);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
UOPGenerateProjectCommandlet::UOPGenerateProjectCommandlet()
|
||||
{
|
||||
LogToConsole = true;
|
||||
}
|
||||
|
||||
FOP_ActionResult UOPGenerateProjectCommandlet::TryCreateProject() const
|
||||
{
|
||||
FText FailReason;
|
||||
FText FailLog;
|
||||
TArray<FString> OutCreatedFiles;
|
||||
|
||||
if (!GameProjectUtils::CreateProject(ProjectInformation, FailReason, FailLog, &OutCreatedFiles))
|
||||
return FOP_ActionResult(EOP_ActionResult::ProjectNotCreated, FailReason);
|
||||
return FOP_ActionResult();
|
||||
}
|
||||
|
||||
FOP_ActionResult UOPGenerateProjectCommandlet::TryLoadProjectDescriptor()
|
||||
{
|
||||
FText FailReason;
|
||||
const bool bLoaded = ProjectDescriptor.Load(ProjectInformation.ProjectFilename, FailReason);
|
||||
|
||||
return FOP_ActionResult(bLoaded ? EOP_ActionResult::Ok : EOP_ActionResult::ProjectNotLoaded, FailReason);
|
||||
}
|
||||
|
||||
void UOPGenerateProjectCommandlet::AttachPluginsToProjectDescriptor()
|
||||
{
|
||||
FPluginReferenceDescriptor OPPluginDescriptor;
|
||||
OPPluginDescriptor.bEnabled = true;
|
||||
OPPluginDescriptor.Name = OPConstants::OP_PluginName;
|
||||
ProjectDescriptor.Plugins.Add(OPPluginDescriptor);
|
||||
|
||||
FPluginReferenceDescriptor PythonPluginDescriptor;
|
||||
PythonPluginDescriptor.bEnabled = true;
|
||||
PythonPluginDescriptor.Name = OPConstants::PythonScript_PluginName;
|
||||
ProjectDescriptor.Plugins.Add(PythonPluginDescriptor);
|
||||
|
||||
FPluginReferenceDescriptor SequencerScriptingPluginDescriptor;
|
||||
SequencerScriptingPluginDescriptor.bEnabled = true;
|
||||
SequencerScriptingPluginDescriptor.Name = OPConstants::SequencerScripting_PluginName;
|
||||
ProjectDescriptor.Plugins.Add(SequencerScriptingPluginDescriptor);
|
||||
|
||||
FPluginReferenceDescriptor MovieRenderPipelinePluginDescriptor;
|
||||
MovieRenderPipelinePluginDescriptor.bEnabled = true;
|
||||
MovieRenderPipelinePluginDescriptor.Name = OPConstants::MovieRenderPipeline_PluginName;
|
||||
ProjectDescriptor.Plugins.Add(MovieRenderPipelinePluginDescriptor);
|
||||
|
||||
FPluginReferenceDescriptor EditorScriptingPluginDescriptor;
|
||||
EditorScriptingPluginDescriptor.bEnabled = true;
|
||||
EditorScriptingPluginDescriptor.Name = OPConstants::EditorScriptingUtils_PluginName;
|
||||
ProjectDescriptor.Plugins.Add(EditorScriptingPluginDescriptor);
|
||||
}
|
||||
|
||||
FOP_ActionResult UOPGenerateProjectCommandlet::TrySave()
|
||||
{
|
||||
FText FailReason;
|
||||
const bool bSaved = ProjectDescriptor.Save(ProjectInformation.ProjectFilename, FailReason);
|
||||
|
||||
return FOP_ActionResult(bSaved ? EOP_ActionResult::Ok : EOP_ActionResult::ProjectNotSaved, FailReason);
|
||||
}
|
||||
|
||||
FOPGenerateProjectParams UOPGenerateProjectCommandlet::ParseParameters(const FString& Params) const
|
||||
{
|
||||
FOPGenerateProjectParams ParamsResult;
|
||||
|
||||
TArray<FString> Tokens, Switches;
|
||||
ParseCommandLine(*Params, Tokens, Switches);
|
||||
|
||||
return ParamsResult;
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2023, Ayon, All rights reserved.
|
||||
|
||||
|
||||
#include "Commandlets/OPActionResult.h"
|
||||
#include "Logging/OP_Log.h"
|
||||
|
||||
EOP_ActionResult::Type& FOP_ActionResult::GetStatus()
|
||||
{
|
||||
return Status;
|
||||
}
|
||||
|
||||
FText& FOP_ActionResult::GetReason()
|
||||
{
|
||||
return Reason;
|
||||
}
|
||||
|
||||
FOP_ActionResult::FOP_ActionResult():Status(EOP_ActionResult::Type::Ok)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
FOP_ActionResult::FOP_ActionResult(const EOP_ActionResult::Type& InEnum):Status(InEnum)
|
||||
{
|
||||
TryLog();
|
||||
}
|
||||
|
||||
FOP_ActionResult::FOP_ActionResult(const EOP_ActionResult::Type& InEnum, const FText& InReason):Status(InEnum), Reason(InReason)
|
||||
{
|
||||
TryLog();
|
||||
};
|
||||
|
||||
bool FOP_ActionResult::IsProblem() const
|
||||
{
|
||||
return Status != EOP_ActionResult::Ok;
|
||||
}
|
||||
|
||||
void FOP_ActionResult::TryLog() const
|
||||
{
|
||||
if(IsProblem())
|
||||
UE_LOG(LogCommandletOPGenerateProject, Error, TEXT("%s"), *Reason.ToString());
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
#include "Logging/OP_Log.h"
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
// Copyright 2023, Ayon, All rights reserved.
|
||||
#include "OpenPype.h"
|
||||
|
||||
#include "ISettingsContainer.h"
|
||||
|
|
@ -16,32 +17,34 @@ static const FName OpenPypeTabName("OpenPype");
|
|||
// This function is triggered when the plugin is staring up
|
||||
void FOpenPypeModule::StartupModule()
|
||||
{
|
||||
FOpenPypeStyle::Initialize();
|
||||
FOpenPypeStyle::SetIcon("Logo", "openpype40");
|
||||
if (!IsRunningCommandlet()) {
|
||||
FOpenPypeStyle::Initialize();
|
||||
FOpenPypeStyle::SetIcon("Logo", "openpype40");
|
||||
|
||||
// Create the Extender that will add content to the menu
|
||||
FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor");
|
||||
// Create the Extender that will add content to the menu
|
||||
FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor");
|
||||
|
||||
TSharedPtr<FExtender> MenuExtender = MakeShareable(new FExtender());
|
||||
TSharedPtr<FExtender> ToolbarExtender = MakeShareable(new FExtender());
|
||||
TSharedPtr<FExtender> MenuExtender = MakeShareable(new FExtender());
|
||||
TSharedPtr<FExtender> ToolbarExtender = MakeShareable(new FExtender());
|
||||
|
||||
MenuExtender->AddMenuExtension(
|
||||
"LevelEditor",
|
||||
EExtensionHook::After,
|
||||
NULL,
|
||||
FMenuExtensionDelegate::CreateRaw(this, &FOpenPypeModule::AddMenuEntry)
|
||||
);
|
||||
ToolbarExtender->AddToolBarExtension(
|
||||
"Settings",
|
||||
EExtensionHook::After,
|
||||
NULL,
|
||||
FToolBarExtensionDelegate::CreateRaw(this, &FOpenPypeModule::AddToobarEntry));
|
||||
MenuExtender->AddMenuExtension(
|
||||
"LevelEditor",
|
||||
EExtensionHook::After,
|
||||
NULL,
|
||||
FMenuExtensionDelegate::CreateRaw(this, &FOpenPypeModule::AddMenuEntry)
|
||||
);
|
||||
ToolbarExtender->AddToolBarExtension(
|
||||
"Settings",
|
||||
EExtensionHook::After,
|
||||
NULL,
|
||||
FToolBarExtensionDelegate::CreateRaw(this, &FOpenPypeModule::AddToobarEntry));
|
||||
|
||||
|
||||
LevelEditorModule.GetMenuExtensibilityManager()->AddExtender(MenuExtender);
|
||||
LevelEditorModule.GetToolBarExtensibilityManager()->AddExtender(ToolbarExtender);
|
||||
LevelEditorModule.GetMenuExtensibilityManager()->AddExtender(MenuExtender);
|
||||
LevelEditorModule.GetToolBarExtensibilityManager()->AddExtender(ToolbarExtender);
|
||||
|
||||
RegisterSettings();
|
||||
RegisterSettings();
|
||||
}
|
||||
}
|
||||
|
||||
void FOpenPypeModule::ShutdownModule()
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
// Copyright 2023, Ayon, All rights reserved.
|
||||
#include "OpenPypeLib.h"
|
||||
|
||||
#include "AssetViewUtils.h"
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
// Copyright 2023, Ayon, All rights reserved.
|
||||
#pragma once
|
||||
|
||||
#include "OpenPypePublishInstance.h"
|
||||
#include "AssetRegistryModule.h"
|
||||
#include "NotificationManager.h"
|
||||
#include "OpenPypeLib.h"
|
||||
#include "OpenPypeSettings.h"
|
||||
#include "SNotificationList.h"
|
||||
#include "Framework/Notifications/NotificationManager.h"
|
||||
#include "Widgets/Notifications/SNotificationList.h"
|
||||
|
||||
//Moves all the invalid pointers to the end to prepare them for the shrinking
|
||||
#define REMOVE_INVALID_ENTRIES(VAR) VAR.CompactStable(); \
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
// Copyright 2023, Ayon, All rights reserved.
|
||||
#include "OpenPypePublishInstanceFactory.h"
|
||||
#include "OpenPypePublishInstance.h"
|
||||
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
// Copyright 2023, Ayon, All rights reserved.
|
||||
#include "OpenPypePythonBridge.h"
|
||||
|
||||
UOpenPypePythonBridge* UOpenPypePythonBridge::Get()
|
||||
|
|
@ -1,9 +1,8 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
// Copyright 2023, Ayon, All rights reserved.
|
||||
|
||||
#include "OpenPypeSettings.h"
|
||||
|
||||
#include "IPluginManager.h"
|
||||
#include "UObjectGlobals.h"
|
||||
#include "Interfaces/IPluginManager.h"
|
||||
|
||||
/**
|
||||
* Mainly is used for initializing default values if the DefaultOpenPypeSettings.ini file does not exist in the saved config
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
// Copyright 2023, Ayon, All rights reserved.
|
||||
#include "OpenPypeStyle.h"
|
||||
#include "Framework/Application/SlateApplication.h"
|
||||
#include "Styling/SlateStyle.h"
|
||||
|
|
@ -43,7 +44,7 @@ const FVector2D Icon40x40(40.0f, 40.0f);
|
|||
TUniquePtr< FSlateStyleSet > FOpenPypeStyle::Create()
|
||||
{
|
||||
TUniquePtr< FSlateStyleSet > Style = MakeUnique<FSlateStyleSet>(GetStyleSetName());
|
||||
Style->SetContentRoot(FPaths::ProjectPluginsDir() / TEXT("OpenPype/Resources"));
|
||||
Style->SetContentRoot(FPaths::EnginePluginsDir() / TEXT("Marketplace/OpenPype/Resources"));
|
||||
|
||||
return Style;
|
||||
}
|
||||
|
|
@ -66,5 +67,4 @@ const ISlateStyle& FOpenPypeStyle::Get()
|
|||
{
|
||||
check(OpenPypeStyleInstance);
|
||||
return *OpenPypeStyleInstance;
|
||||
return *OpenPypeStyleInstance;
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
// Copyright 2023, Ayon, All rights reserved.
|
||||
#pragma once
|
||||
|
||||
#include "GameProjectUtils.h"
|
||||
#include "Commandlets/OPActionResult.h"
|
||||
#include "ProjectDescriptor.h"
|
||||
#include "Commandlets/Commandlet.h"
|
||||
#include "OPGenerateProjectCommandlet.generated.h"
|
||||
|
||||
struct FProjectDescriptor;
|
||||
struct FProjectInformation;
|
||||
|
||||
/**
|
||||
* @brief Structure which parses command line parameters and generates FProjectInformation
|
||||
*/
|
||||
USTRUCT()
|
||||
struct FOPGenerateProjectParams
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
private:
|
||||
FString CommandLineParams;
|
||||
TArray<FString> Tokens;
|
||||
TArray<FString> Switches;
|
||||
|
||||
public:
|
||||
FOPGenerateProjectParams();
|
||||
FOPGenerateProjectParams(const FString& CommandLineParams);
|
||||
|
||||
FProjectInformation GenerateUEProjectInformation() const;
|
||||
|
||||
private:
|
||||
FString TryGetToken(const int32 Index) const;
|
||||
FString GetProjectFileName() const;
|
||||
|
||||
bool IsSwitchPresent(const FString& Switch) const;
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class OPENPYPE_API UOPGenerateProjectCommandlet : public UCommandlet
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
private:
|
||||
FProjectInformation ProjectInformation;
|
||||
FProjectDescriptor ProjectDescriptor;
|
||||
|
||||
public:
|
||||
UOPGenerateProjectCommandlet();
|
||||
|
||||
virtual int32 Main(const FString& CommandLineParams) override;
|
||||
|
||||
private:
|
||||
FOPGenerateProjectParams ParseParameters(const FString& Params) const;
|
||||
FOP_ActionResult TryCreateProject() const;
|
||||
FOP_ActionResult TryLoadProjectDescriptor();
|
||||
void AttachPluginsToProjectDescriptor();
|
||||
FOP_ActionResult TrySave();
|
||||
};
|
||||
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
// Copyright 2023, Ayon, All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "OPActionResult.generated.h"
|
||||
|
||||
/**
|
||||
* @brief This macro returns error code when is problem or does nothing when there is no problem.
|
||||
* @param ActionResult FOP_ActionResult structure
|
||||
*/
|
||||
#define EVALUATE_OP_ACTION_RESULT(ActionResult) \
|
||||
if(ActionResult.IsProblem()) \
|
||||
return ActionResult.GetStatus();
|
||||
|
||||
/**
|
||||
* @brief This enum values are humanly readable mapping of error codes.
|
||||
* Here should be all error codes to be possible find what went wrong.
|
||||
* TODO: In the future should exists an web document where is mapped error code & what problem occured & how to repair it...
|
||||
*/
|
||||
UENUM()
|
||||
namespace EOP_ActionResult
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
Ok,
|
||||
ProjectNotCreated,
|
||||
ProjectNotLoaded,
|
||||
ProjectNotSaved,
|
||||
//....Here insert another values
|
||||
|
||||
//Do not remove!
|
||||
//Usable for looping through enum values
|
||||
__Last UMETA(Hidden)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief This struct holds action result enum and optionally reason of fail
|
||||
*/
|
||||
USTRUCT()
|
||||
struct FOP_ActionResult
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/** @brief Default constructor usable when there is no problem */
|
||||
FOP_ActionResult();
|
||||
|
||||
/**
|
||||
* @brief This constructor initializes variables & attempts to log when is error
|
||||
* @param InEnum Status
|
||||
*/
|
||||
FOP_ActionResult(const EOP_ActionResult::Type& InEnum);
|
||||
|
||||
/**
|
||||
* @brief This constructor initializes variables & attempts to log when is error
|
||||
* @param InEnum Status
|
||||
* @param InReason Reason of potential fail
|
||||
*/
|
||||
FOP_ActionResult(const EOP_ActionResult::Type& InEnum, const FText& InReason);
|
||||
|
||||
private:
|
||||
/** @brief Action status */
|
||||
EOP_ActionResult::Type Status;
|
||||
|
||||
/** @brief Optional reason of fail */
|
||||
FText Reason;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Checks if there is problematic state
|
||||
* @return true when status is not equal to EOP_ActionResult::Ok
|
||||
*/
|
||||
bool IsProblem() const;
|
||||
EOP_ActionResult::Type& GetStatus();
|
||||
FText& GetReason();
|
||||
|
||||
private:
|
||||
void TryLog() const;
|
||||
};
|
||||
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
// Copyright 2023, Ayon, All rights reserved.
|
||||
#pragma once
|
||||
|
||||
DEFINE_LOG_CATEGORY_STATIC(LogCommandletOPGenerateProject, Log, All);
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright 2023, Ayon, All rights reserved.
|
||||
#pragma once
|
||||
|
||||
namespace OPConstants
|
||||
{
|
||||
const FString OP_PluginName = "OpenPype";
|
||||
const FString PythonScript_PluginName = "PythonScriptPlugin";
|
||||
const FString SequencerScripting_PluginName = "SequencerScripting";
|
||||
const FString MovieRenderPipeline_PluginName = "MovieRenderPipeline";
|
||||
const FString EditorScriptingUtils_PluginName = "EditorScriptingUtilities";
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
|
||||
// Copyright 2023, Ayon, All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
// Copyright 2023, Ayon, All rights reserved.
|
||||
#pragma once
|
||||
|
||||
#include "Engine.h"
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
// Copyright 2023, Ayon, All rights reserved.
|
||||
#pragma once
|
||||
|
||||
#include "Engine.h"
|
||||
|
|
@ -16,7 +17,7 @@ public:
|
|||
*
|
||||
* @return - Set of UObjects. Careful! They are returning raw pointers. Seems like an issue in UE5
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure)
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="Python")
|
||||
TSet<UObject*> GetInternalAssets() const
|
||||
{
|
||||
//For some reason it can only return Raw Pointers? Seems like an issue which they haven't fixed.
|
||||
|
|
@ -33,7 +34,7 @@ public:
|
|||
*
|
||||
* @return - TSet of assets (UObjects). Careful! They are returning raw pointers. Seems like an issue in UE5
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure)
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="Python")
|
||||
TSet<UObject*> GetExternalAssets() const
|
||||
{
|
||||
//For some reason it can only return Raw Pointers? Seems like an issue which they haven't fixed.
|
||||
|
|
@ -53,7 +54,7 @@ public:
|
|||
*
|
||||
* @attention If the bAddExternalAssets variable is false, external assets won't be included!
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure)
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="Python")
|
||||
TSet<UObject*> GetAllAssets() const
|
||||
{
|
||||
const TSet<TSoftObjectPtr<UObject>>& IteratedSet = bAddExternalAssets
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
// Copyright 2023, Ayon, All rights reserved.
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
// Copyright 2023, Ayon, All rights reserved.
|
||||
#pragma once
|
||||
#include "Engine.h"
|
||||
#include "OpenPypePythonBridge.generated.h"
|
||||
|
|
@ -1,9 +1,8 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
// Copyright 2023, Ayon, All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Object.h"
|
||||
#include "OpenPypeSettings.generated.h"
|
||||
|
||||
#define OPENPYPE_SETTINGS_FILEPATH IPluginManager::Get().FindPlugin("OpenPype")->GetBaseDir() / TEXT("Config") / TEXT("DefaultOpenPypeSettings.ini")
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
// Copyright 2023, Ayon, All rights reserved.
|
||||
#pragma once
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
|
|
@ -1,115 +0,0 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#include "AssetContainer.h"
|
||||
#include "AssetRegistryModule.h"
|
||||
#include "Misc/PackageName.h"
|
||||
#include "Engine.h"
|
||||
#include "Containers/UnrealString.h"
|
||||
|
||||
UAssetContainer::UAssetContainer(const FObjectInitializer& ObjectInitializer)
|
||||
: UAssetUserData(ObjectInitializer)
|
||||
{
|
||||
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
|
||||
FString path = UAssetContainer::GetPathName();
|
||||
UE_LOG(LogTemp, Warning, TEXT("UAssetContainer %s"), *path);
|
||||
FARFilter Filter;
|
||||
Filter.PackagePaths.Add(FName(*path));
|
||||
|
||||
AssetRegistryModule.Get().OnAssetAdded().AddUObject(this, &UAssetContainer::OnAssetAdded);
|
||||
AssetRegistryModule.Get().OnAssetRemoved().AddUObject(this, &UAssetContainer::OnAssetRemoved);
|
||||
AssetRegistryModule.Get().OnAssetRenamed().AddUObject(this, &UAssetContainer::OnAssetRenamed);
|
||||
}
|
||||
|
||||
void UAssetContainer::OnAssetAdded(const FAssetData& AssetData)
|
||||
{
|
||||
TArray<FString> split;
|
||||
|
||||
// get directory of current container
|
||||
FString selfFullPath = UAssetContainer::GetPathName();
|
||||
FString selfDir = FPackageName::GetLongPackagePath(*selfFullPath);
|
||||
|
||||
// get asset path and class
|
||||
FString assetPath = AssetData.GetFullName();
|
||||
FString assetFName = AssetData.AssetClass.ToString();
|
||||
|
||||
// split path
|
||||
assetPath.ParseIntoArray(split, TEXT(" "), true);
|
||||
|
||||
FString assetDir = FPackageName::GetLongPackagePath(*split[1]);
|
||||
|
||||
// take interest only in paths starting with path of current container
|
||||
if (assetDir.StartsWith(*selfDir))
|
||||
{
|
||||
// exclude self
|
||||
if (assetFName != "AssetContainer")
|
||||
{
|
||||
assets.Add(assetPath);
|
||||
assetsData.Add(AssetData);
|
||||
UE_LOG(LogTemp, Log, TEXT("%s: asset added to %s"), *selfFullPath, *selfDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UAssetContainer::OnAssetRemoved(const FAssetData& AssetData)
|
||||
{
|
||||
TArray<FString> split;
|
||||
|
||||
// get directory of current container
|
||||
FString selfFullPath = UAssetContainer::GetPathName();
|
||||
FString selfDir = FPackageName::GetLongPackagePath(*selfFullPath);
|
||||
|
||||
// get asset path and class
|
||||
FString assetPath = AssetData.GetFullName();
|
||||
FString assetFName = AssetData.AssetClass.ToString();
|
||||
|
||||
// split path
|
||||
assetPath.ParseIntoArray(split, TEXT(" "), true);
|
||||
|
||||
FString assetDir = FPackageName::GetLongPackagePath(*split[1]);
|
||||
|
||||
// take interest only in paths starting with path of current container
|
||||
FString path = UAssetContainer::GetPathName();
|
||||
FString lpp = FPackageName::GetLongPackagePath(*path);
|
||||
|
||||
if (assetDir.StartsWith(*selfDir))
|
||||
{
|
||||
// exclude self
|
||||
if (assetFName != "AssetContainer")
|
||||
{
|
||||
// UE_LOG(LogTemp, Warning, TEXT("%s: asset removed"), *lpp);
|
||||
assets.Remove(assetPath);
|
||||
assetsData.Remove(AssetData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UAssetContainer::OnAssetRenamed(const FAssetData& AssetData, const FString& str)
|
||||
{
|
||||
TArray<FString> split;
|
||||
|
||||
// get directory of current container
|
||||
FString selfFullPath = UAssetContainer::GetPathName();
|
||||
FString selfDir = FPackageName::GetLongPackagePath(*selfFullPath);
|
||||
|
||||
// get asset path and class
|
||||
FString assetPath = AssetData.GetFullName();
|
||||
FString assetFName = AssetData.AssetClass.ToString();
|
||||
|
||||
// split path
|
||||
assetPath.ParseIntoArray(split, TEXT(" "), true);
|
||||
|
||||
FString assetDir = FPackageName::GetLongPackagePath(*split[1]);
|
||||
if (assetDir.StartsWith(*selfDir))
|
||||
{
|
||||
// exclude self
|
||||
if (assetFName != "AssetContainer")
|
||||
{
|
||||
|
||||
assets.Remove(str);
|
||||
assets.Add(assetPath);
|
||||
assetsData.Remove(AssetData);
|
||||
// UE_LOG(LogTemp, Warning, TEXT("%s: asset renamed %s"), *lpp, *str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/NoExportTypes.h"
|
||||
#include "Engine/AssetUserData.h"
|
||||
#include "AssetData.h"
|
||||
#include "AssetContainer.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS(Blueprintable)
|
||||
class OPENPYPE_API UAssetContainer : public UAssetUserData
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UAssetContainer(const FObjectInitializer& ObjectInitalizer);
|
||||
// ~UAssetContainer();
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly)
|
||||
TArray<FString> assets;
|
||||
|
||||
// There seems to be no reflection option to expose array of FAssetData
|
||||
/*
|
||||
UPROPERTY(Transient, BlueprintReadOnly, Category = "Python", meta=(DisplayName="Assets Data"))
|
||||
TArray<FAssetData> assetsData;
|
||||
*/
|
||||
private:
|
||||
TArray<FAssetData> assetsData;
|
||||
void OnAssetAdded(const FAssetData& AssetData);
|
||||
void OnAssetRemoved(const FAssetData& AssetData);
|
||||
void OnAssetRenamed(const FAssetData& AssetData, const FString& str);
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Factories/Factory.h"
|
||||
#include "AssetContainerFactory.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class OPENPYPE_API UAssetContainerFactory : public UFactory
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UAssetContainerFactory(const FObjectInitializer& ObjectInitializer);
|
||||
virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override;
|
||||
virtual bool ShouldShowInNewMenu() const override;
|
||||
};
|
||||
41
openpype/hosts/unreal/integration/UE_5.0/CommandletProject/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
*.smod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
/Saved
|
||||
/DerivedDataCache
|
||||
/Intermediate
|
||||
/Binaries
|
||||
/Content
|
||||
/Config
|
||||
/.idea
|
||||
/.vs
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"FileVersion": 3,
|
||||
"EngineAssociation": "5.0",
|
||||
"Category": "",
|
||||
"Description": "",
|
||||
"Plugins": [
|
||||
{
|
||||
"Name": "ModelingToolsEditorMode",
|
||||
"Enabled": true,
|
||||
"TargetAllowList": [
|
||||
"Editor"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "OpenPype",
|
||||
"Enabled": true,
|
||||
"Type": "Editor"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
[FilterPlugin]
|
||||
; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and
|
||||
; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively.
|
||||
;
|
||||
; Examples:
|
||||
; /README.txt
|
||||
; /Extras/...
|
||||
; /Binaries/ThirdParty/*.dll
|
||||
|
|
@ -11,9 +11,9 @@
|
|||
"MarketplaceURL": "",
|
||||
"SupportURL": "https://pype.club/",
|
||||
"CanContainContent": true,
|
||||
"IsBetaVersion": true,
|
||||
"EngineVersion": "5.0",
|
||||
"IsExperimentalVersion": false,
|
||||
"Installed": false,
|
||||
"Installed": true,
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "OpenPype",
|
||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
|
||||
// Copyright 2023, Ayon, All rights reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
|
|
@ -10,7 +10,7 @@ public class OpenPype : ModuleRules
|
|||
bLegacyPublicIncludePaths = false;
|
||||
ShadowVariableWarningLevel = WarningLevel.Error;
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
IncludeOrderVersion = EngineIncludeOrderVersion.Unreal5_0;
|
||||
//IncludeOrderVersion = EngineIncludeOrderVersion.Unreal5_0;
|
||||
|
||||
PublicIncludePaths.AddRange(
|
||||
new string[] {
|
||||
|
|
@ -30,14 +30,15 @@ public class OpenPype : ModuleRules
|
|||
new string[]
|
||||
{
|
||||
"Core",
|
||||
"CoreUObject"
|
||||
// ... add other public dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"GameProjectGeneration",
|
||||
"Projects",
|
||||
"InputCore",
|
||||
"EditorFramework",
|
||||
|
|
@ -14,7 +14,7 @@ UAssetContainer::UAssetContainer(const FObjectInitializer& ObjectInitializer)
|
|||
UE_LOG(LogTemp, Warning, TEXT("UAssetContainer %s"), *path);
|
||||
FARFilter Filter;
|
||||
Filter.PackagePaths.Add(FName(*path));
|
||||
|
||||
|
||||
AssetRegistryModule.Get().OnAssetAdded().AddUObject(this, &UAssetContainer::OnAssetAdded);
|
||||
AssetRegistryModule.Get().OnAssetRemoved().AddUObject(this, &UAssetContainer::OnAssetRemoved);
|
||||
AssetRegistryModule.Get().OnAssetRenamed().AddUObject(this, &UAssetContainer::OnAssetRenamed);
|
||||
|
|
@ -36,7 +36,7 @@ void UAssetContainer::OnAssetAdded(const FAssetData& AssetData)
|
|||
assetPath.ParseIntoArray(split, TEXT(" "), true);
|
||||
|
||||
FString assetDir = FPackageName::GetLongPackagePath(*split[1]);
|
||||
|
||||
|
||||
// take interest only in paths starting with path of current container
|
||||
if (assetDir.StartsWith(*selfDir))
|
||||
{
|
||||
|
|
@ -112,4 +112,3 @@ void UAssetContainer::OnAssetRenamed(const FAssetData& AssetData, const FString&
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
// Copyright 2023, Ayon, All rights reserved.
|
||||
#include "Commandlets/Implementations/OPGenerateProjectCommandlet.h"
|
||||
|
||||
#include "Editor.h"
|
||||
#include "GameProjectUtils.h"
|
||||
#include "OPConstants.h"
|
||||
#include "Commandlets/OPActionResult.h"
|
||||
#include "ProjectDescriptor.h"
|
||||
|
||||
int32 UOPGenerateProjectCommandlet::Main(const FString& CommandLineParams)
|
||||
{
|
||||
//Parses command line parameters & creates structure FProjectInformation
|
||||
const FOPGenerateProjectParams ParsedParams = FOPGenerateProjectParams(CommandLineParams);
|
||||
ProjectInformation = ParsedParams.GenerateUEProjectInformation();
|
||||
|
||||
//Creates .uproject & other UE files
|
||||
EVALUATE_OP_ACTION_RESULT(TryCreateProject());
|
||||
|
||||
//Loads created .uproject
|
||||
EVALUATE_OP_ACTION_RESULT(TryLoadProjectDescriptor());
|
||||
|
||||
//Adds needed plugin to .uproject
|
||||
AttachPluginsToProjectDescriptor();
|
||||
|
||||
//Saves .uproject
|
||||
EVALUATE_OP_ACTION_RESULT(TrySave());
|
||||
|
||||
//When we are here, there should not be problems in generating Unreal Project for OpenPype
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
FOPGenerateProjectParams::FOPGenerateProjectParams(): FOPGenerateProjectParams("")
|
||||
{
|
||||
}
|
||||
|
||||
FOPGenerateProjectParams::FOPGenerateProjectParams(const FString& CommandLineParams): CommandLineParams(
|
||||
CommandLineParams)
|
||||
{
|
||||
UCommandlet::ParseCommandLine(*CommandLineParams, Tokens, Switches);
|
||||
}
|
||||
|
||||
FProjectInformation FOPGenerateProjectParams::GenerateUEProjectInformation() const
|
||||
{
|
||||
FProjectInformation ProjectInformation = FProjectInformation();
|
||||
ProjectInformation.ProjectFilename = GetProjectFileName();
|
||||
|
||||
ProjectInformation.bShouldGenerateCode = IsSwitchPresent("GenerateCode");
|
||||
|
||||
return ProjectInformation;
|
||||
}
|
||||
|
||||
FString FOPGenerateProjectParams::TryGetToken(const int32 Index) const
|
||||
{
|
||||
return Tokens.IsValidIndex(Index) ? Tokens[Index] : "";
|
||||
}
|
||||
|
||||
FString FOPGenerateProjectParams::GetProjectFileName() const
|
||||
{
|
||||
return TryGetToken(0);
|
||||
}
|
||||
|
||||
bool FOPGenerateProjectParams::IsSwitchPresent(const FString& Switch) const
|
||||
{
|
||||
return INDEX_NONE != Switches.IndexOfByPredicate([&Switch](const FString& Item) -> bool
|
||||
{
|
||||
return Item.Equals(Switch);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
UOPGenerateProjectCommandlet::UOPGenerateProjectCommandlet()
|
||||
{
|
||||
LogToConsole = true;
|
||||
}
|
||||
|
||||
FOP_ActionResult UOPGenerateProjectCommandlet::TryCreateProject() const
|
||||
{
|
||||
FText FailReason;
|
||||
FText FailLog;
|
||||
TArray<FString> OutCreatedFiles;
|
||||
|
||||
if (!GameProjectUtils::CreateProject(ProjectInformation, FailReason, FailLog, &OutCreatedFiles))
|
||||
return FOP_ActionResult(EOP_ActionResult::ProjectNotCreated, FailReason);
|
||||
return FOP_ActionResult();
|
||||
}
|
||||
|
||||
FOP_ActionResult UOPGenerateProjectCommandlet::TryLoadProjectDescriptor()
|
||||
{
|
||||
FText FailReason;
|
||||
const bool bLoaded = ProjectDescriptor.Load(ProjectInformation.ProjectFilename, FailReason);
|
||||
|
||||
return FOP_ActionResult(bLoaded ? EOP_ActionResult::Ok : EOP_ActionResult::ProjectNotLoaded, FailReason);
|
||||
}
|
||||
|
||||
void UOPGenerateProjectCommandlet::AttachPluginsToProjectDescriptor()
|
||||
{
|
||||
FPluginReferenceDescriptor OPPluginDescriptor;
|
||||
OPPluginDescriptor.bEnabled = true;
|
||||
OPPluginDescriptor.Name = OPConstants::OP_PluginName;
|
||||
ProjectDescriptor.Plugins.Add(OPPluginDescriptor);
|
||||
|
||||
FPluginReferenceDescriptor PythonPluginDescriptor;
|
||||
PythonPluginDescriptor.bEnabled = true;
|
||||
PythonPluginDescriptor.Name = OPConstants::PythonScript_PluginName;
|
||||
ProjectDescriptor.Plugins.Add(PythonPluginDescriptor);
|
||||
|
||||
FPluginReferenceDescriptor SequencerScriptingPluginDescriptor;
|
||||
SequencerScriptingPluginDescriptor.bEnabled = true;
|
||||
SequencerScriptingPluginDescriptor.Name = OPConstants::SequencerScripting_PluginName;
|
||||
ProjectDescriptor.Plugins.Add(SequencerScriptingPluginDescriptor);
|
||||
|
||||
FPluginReferenceDescriptor MovieRenderPipelinePluginDescriptor;
|
||||
MovieRenderPipelinePluginDescriptor.bEnabled = true;
|
||||
MovieRenderPipelinePluginDescriptor.Name = OPConstants::MovieRenderPipeline_PluginName;
|
||||
ProjectDescriptor.Plugins.Add(MovieRenderPipelinePluginDescriptor);
|
||||
|
||||
FPluginReferenceDescriptor EditorScriptingPluginDescriptor;
|
||||
EditorScriptingPluginDescriptor.bEnabled = true;
|
||||
EditorScriptingPluginDescriptor.Name = OPConstants::EditorScriptingUtils_PluginName;
|
||||
ProjectDescriptor.Plugins.Add(EditorScriptingPluginDescriptor);
|
||||
}
|
||||
|
||||
FOP_ActionResult UOPGenerateProjectCommandlet::TrySave()
|
||||
{
|
||||
FText FailReason;
|
||||
const bool bSaved = ProjectDescriptor.Save(ProjectInformation.ProjectFilename, FailReason);
|
||||
|
||||
return FOP_ActionResult(bSaved ? EOP_ActionResult::Ok : EOP_ActionResult::ProjectNotSaved, FailReason);
|
||||
}
|
||||
|
||||
FOPGenerateProjectParams UOPGenerateProjectCommandlet::ParseParameters(const FString& Params) const
|
||||
{
|
||||
FOPGenerateProjectParams ParamsResult;
|
||||
|
||||
TArray<FString> Tokens, Switches;
|
||||
ParseCommandLine(*Params, Tokens, Switches);
|
||||
|
||||
return ParamsResult;
|
||||
}
|
||||