Merge branch 'chore/AY-4916_Move-Houdini-client-code' into chore/AY-4916_Move-Houdini-client-code-settings-category

This commit is contained in:
MustafaJafar 2024-05-23 18:25:11 +03:00
commit 0faea9903d
95 changed files with 583 additions and 112 deletions

View file

@ -0,0 +1,141 @@
# -*- coding: utf-8 -*-
"""Creator plugin for creating Model product type.
Note:
Currently, This creator plugin is the same as 'create_pointcache.py'
But renaming the product type to 'model'.
It's purpose to support
Maya (load/publish model from maya to/from houdini).
It's considered to support multiple representations in the future.
"""
from ayon_houdini.api import plugin
from ayon_core.lib import BoolDef
import hou
class CreateModel(plugin.HoudiniCreator):
"""Create Model"""
identifier = "io.openpype.creators.houdini.model"
label = "Model"
product_type = "model"
icon = "cube"
def create(self, product_name, instance_data, pre_create_data):
instance_data.pop("active", None)
instance_data.update({"node_type": "alembic"})
creator_attributes = instance_data.setdefault(
"creator_attributes", dict())
creator_attributes["farm"] = pre_create_data["farm"]
instance = super(CreateModel, self).create(
product_name,
instance_data,
pre_create_data)
instance_node = hou.node(instance.get("instance_node"))
parms = {
"use_sop_path": True,
"build_from_path": True,
"path_attrib": "path",
"prim_to_detail_pattern": "cbId",
"format": 2,
"facesets": 0,
"filename": hou.text.expandString(
"$HIP/pyblish/{}.abc".format(product_name))
}
if self.selected_nodes:
selected_node = self.selected_nodes[0]
# Although Houdini allows ObjNode path on `sop_path` for the
# the ROP node we prefer it set to the SopNode path explicitly
# Allow sop level paths (e.g. /obj/geo1/box1)
if isinstance(selected_node, hou.SopNode):
parms["sop_path"] = selected_node.path()
self.log.debug(
"Valid SopNode selection, 'SOP Path' in ROP will be set to '%s'."
% selected_node.path()
)
# Allow object level paths to Geometry nodes (e.g. /obj/geo1)
# but do not allow other object level nodes types like cameras, etc.
elif isinstance(selected_node, hou.ObjNode) and \
selected_node.type().name() in ["geo"]:
# get the output node with the minimum
# 'outputidx' or the node with display flag
sop_path = self.get_obj_output(selected_node)
if sop_path:
parms["sop_path"] = sop_path.path()
self.log.debug(
"Valid ObjNode selection, 'SOP Path' in ROP will be set to "
"the child path '%s'."
% sop_path.path()
)
if not parms.get("sop_path", None):
self.log.debug(
"Selection isn't valid. 'SOP Path' in ROP will be empty."
)
else:
self.log.debug(
"No Selection. 'SOP Path' in ROP will be empty."
)
instance_node.setParms(parms)
instance_node.parm("trange").set(1)
# Explicitly set f1 and f2 to frame start.
# Which forces the rop node to export one frame.
instance_node.parmTuple('f').deleteAllKeyframes()
fstart = int(hou.hscriptExpression("$FSTART"))
instance_node.parmTuple('f').set((fstart, fstart, 1))
# Lock any parameters in this list
to_lock = ["prim_to_detail_pattern"]
self.lock_parameters(instance_node, to_lock)
def get_network_categories(self):
return [
hou.ropNodeTypeCategory(),
hou.sopNodeTypeCategory()
]
def get_obj_output(self, obj_node):
"""Find output node with the smallest 'outputidx'."""
outputs = obj_node.subnetOutputs()
# if obj_node is empty
if not outputs:
return
# if obj_node has one output child whether its
# sop output node or a node with the render flag
elif len(outputs) == 1:
return outputs[0]
# if there are more than one, then it have multiple output nodes
# return the one with the minimum 'outputidx'
else:
return min(outputs,
key=lambda node: node.evalParm('outputidx'))
def get_instance_attr_defs(self):
return [
BoolDef("farm",
label="Submitting to Farm",
default=False)
]
def get_pre_create_attr_defs(self):
attrs = super().get_pre_create_attr_defs()
# Use same attributes as for instance attributes
return attrs + self.get_instance_attr_defs()

View file

@ -12,7 +12,7 @@ class CollectDataforCache(plugin.HoudiniInstancePlugin):
order = pyblish.api.CollectorOrder + 0.11
families = ["ass", "pointcache",
"mantraifd", "redshiftproxy",
"vdbcache"]
"vdbcache", "model"]
hosts = ["houdini"]
targets = ["local", "remote"]
label = "Collect Data for Cache"
@ -44,10 +44,7 @@ class CollectDataforCache(plugin.HoudiniInstancePlugin):
cache_files = {"_": instance.data["files"]}
# Convert instance family to pointcache if it is bgeo or abc
# because ???
for family in instance.data["families"]:
if family == "bgeo" or "abc":
instance.data["productType"] = "pointcache"
break
self.log.debug(instance.data["families"])
instance.data.update({
"plugin": "Houdini",
"publish": True

View file

@ -11,7 +11,7 @@ class CollectChunkSize(plugin.HoudiniInstancePlugin,
order = pyblish.api.CollectorOrder + 0.05
families = ["ass", "pointcache",
"vdbcache", "mantraifd",
"redshiftproxy"]
"redshiftproxy", "model"]
hosts = ["houdini"]
targets = ["local", "remote"]
label = "Collect Chunk Size"

View file

@ -0,0 +1,24 @@
"""Collector for different types.
This will add additional families to different instance based on
the creator_identifier parameter.
"""
import pyblish.api
class CollectPointcacheType(pyblish.api.InstancePlugin):
"""Collect data type for different instances."""
order = pyblish.api.CollectorOrder
hosts = ["houdini"]
families = ["pointcache", "model"]
label = "Collect instances types"
def process(self, instance):
if instance.data["creator_identifier"] == "io.openpype.creators.houdini.bgeo": # noqa: E501
instance.data["families"] += ["bgeo"]
elif instance.data["creator_identifier"] in {
"io.openpype.creators.houdini.pointcache",
"io.openpype.creators.houdini.model"
}:
instance.data["families"] += ["abc"]

View file

@ -15,7 +15,8 @@ class CollectOutputSOPPath(plugin.HoudiniInstancePlugin):
"usd",
"usdrender",
"redshiftproxy",
"staticMesh"
"staticMesh",
"model"
]
hosts = ["houdini"]

View file

@ -1,21 +0,0 @@
"""Collector for pointcache types.
This will add additional family to pointcache instance based on
the creator_identifier parameter.
"""
import pyblish.api
from ayon_houdini.api import plugin
class CollectPointcacheType(plugin.HoudiniInstancePlugin):
"""Collect data type for pointcache instance."""
order = pyblish.api.CollectorOrder
hosts = ["houdini"]
families = ["pointcache"]
label = "Collect type of pointcache"
def process(self, instance):
if instance.data["creator_identifier"] == "io.openpype.creators.houdini.bgeo": # noqa: E501
instance.data["families"] += ["bgeo"]
elif instance.data["creator_identifier"] == "io.openpype.creators.houdini.pointcache": # noqa: E501
instance.data["families"] += ["abc"]

View file

@ -0,0 +1,60 @@
# -*- coding: utf-8 -*-
"""Validator for checking that export is a single frame."""
import pyblish.api
from ayon_core.pipeline import (
PublishValidationError,
OptionalPyblishPluginMixin
)
from ayon_core.pipeline.publish import ValidateContentsOrder
from ayon_houdini.api.action import SelectInvalidAction
class ValidateSingleFrame(pyblish.api.InstancePlugin,
OptionalPyblishPluginMixin):
"""Validate Export is a Single Frame.
It checks if rop node is exporting one frame.
This is mainly for Model product type.
"""
families = ["model"]
hosts = ["houdini"]
label = "Validate Single Frame"
order = ValidateContentsOrder + 0.1
actions = [SelectInvalidAction]
def process(self, instance):
invalid = self.get_invalid(instance)
if invalid:
nodes = [n.path() for n in invalid]
raise PublishValidationError(
"See log for details. "
"Invalid nodes: {0}".format(nodes)
)
@classmethod
def get_invalid(cls, instance):
invalid = []
frame_start = instance.data.get("frameStartHandle")
frame_end = instance.data.get("frameEndHandle")
# This happens if instance node has no 'trange' parameter.
if frame_start is None or frame_end is None:
cls.log.debug(
"No frame data, skipping check.."
)
return
if frame_start != frame_end:
invalid.append(instance.data["instance_node"])
cls.log.error(
"Invalid frame range on '%s'."
"You should use the same frame number for 'f1' "
"and 'f2' parameters.",
instance.data["instance_node"].path()
)
return invalid

View file

@ -16,9 +16,13 @@ class ValidateMeshIsStatic(plugin.HoudiniInstancePlugin,
"""Validate mesh is static.
It checks if output node is time dependent.
this avoids getting different output from ROP node when extracted
from a different frame than the first frame.
(Might be overly restrictive though)
"""
families = ["staticMesh"]
families = ["staticMesh",
"model"]
hosts = ["houdini"]
label = "Validate Mesh is Static"
order = ValidateContentsOrder + 0.1

View file

@ -8,7 +8,7 @@ class ValidateIntermediateDirectoriesChecked(plugin.HoudiniInstancePlugin):
"""Validate Create Intermediate Directories is enabled on ROP node."""
order = pyblish.api.ValidatorOrder
families = ["pointcache", "camera", "vdbcache"]
families = ["pointcache", "camera", "vdbcache", "model"]
hosts = ["houdini"]
label = "Create Intermediate Directories Checked"

View file

@ -24,7 +24,7 @@ class ValidateSopOutputNode(plugin.HoudiniInstancePlugin):
"""
order = pyblish.api.ValidatorOrder
families = ["pointcache", "vdbcache"]
families = ["pointcache", "vdbcache", "model"]
hosts = ["houdini"]
label = "Validate Output Node (SOP)"
actions = [SelectROPAction, SelectInvalidAction]

View file

@ -5,6 +5,6 @@ version = "0.3.0"
client_dir = "ayon_houdini"
ayon_required_addons = {
"core": ">0.3.1",
"core": ">0.3.2",
}
ayon_compatible_addons = {}

View file

@ -57,6 +57,9 @@ class CreatePluginsModel(BaseSettingsModel):
CreateMantraROP: CreatorModel = SettingsField(
default_factory=CreatorModel,
title="Create Mantra ROP")
CreateModel: CreatorModel = SettingsField(
default_factory=CreatorModel,
title="Create Model")
CreatePointCache: CreatorModel = SettingsField(
default_factory=CreatorModel,
title="Create PointCache (Abc)")
@ -124,6 +127,10 @@ DEFAULT_HOUDINI_CREATE_SETTINGS = {
"enabled": True,
"default_variants": ["Main"]
},
"CreateModel": {
"enabled": True,
"default_variants": ["Main"]
},
"CreatePointCache": {
"enabled": True,
"default_variants": ["Main"]