Merge pull request #4588 from ynput/feature/OP-3129_houdini-bgeo-publishing

This commit is contained in:
Ondřej Samohel 2023-07-17 16:38:02 +02:00 committed by GitHub
commit b26b5fdb88
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 228 additions and 12 deletions

View file

@ -69,6 +69,8 @@ class HoudiniLegacyConvertor(SubsetConvertorPlugin):
"creator_identifier": self.family_to_id[family],
"instance_node": subset.path()
}
if family == "pointcache":
data["families"] = ["abc"]
self.log.info("Converting {} to {}".format(
subset.path(), self.family_to_id[family]))
imprint(subset, data)

View file

@ -0,0 +1,92 @@
# -*- coding: utf-8 -*-
"""Creator plugin for creating pointcache bgeo files."""
from openpype.hosts.houdini.api import plugin
from openpype.pipeline import CreatedInstance, CreatorError
from openpype.lib import EnumDef
class CreateBGEO(plugin.HoudiniCreator):
"""BGEO pointcache creator."""
identifier = "io.openpype.creators.houdini.bgeo"
label = "BGEO PointCache"
family = "pointcache"
icon = "gears"
def create(self, subset_name, instance_data, pre_create_data):
import hou
instance_data.pop("active", None)
instance_data.update({"node_type": "geometry"})
instance = super(CreateBGEO, self).create(
subset_name,
instance_data,
pre_create_data) # type: CreatedInstance
instance_node = hou.node(instance.get("instance_node"))
file_path = "{}{}".format(
hou.text.expandString("$HIP/pyblish/"),
"{}.$F4.{}".format(
subset_name,
pre_create_data.get("bgeo_type") or "bgeo.sc")
)
parms = {
"sopoutput": file_path
}
instance_node.parm("trange").set(1)
if self.selected_nodes:
# if selection is on SOP level, use it
if isinstance(self.selected_nodes[0], hou.SopNode):
parms["soppath"] = self.selected_nodes[0].path()
else:
# try to find output node with the lowest index
outputs = [
child for child in self.selected_nodes[0].children()
if child.type().name() == "output"
]
if not outputs:
instance_node.setParms(parms)
raise CreatorError((
"Missing output node in SOP level for the selection. "
"Please select correct SOP path in created instance."
))
outputs.sort(key=lambda output: output.evalParm("outputidx"))
parms["soppath"] = outputs[0].path()
instance_node.setParms(parms)
def get_pre_create_attr_defs(self):
attrs = super().get_pre_create_attr_defs()
bgeo_enum = [
{
"value": "bgeo",
"label": "uncompressed bgeo (.bgeo)"
},
{
"value": "bgeosc",
"label": "BLOSC compressed bgeo (.bgeosc)"
},
{
"value": "bgeo.sc",
"label": "BLOSC compressed bgeo (.bgeo.sc)"
},
{
"value": "bgeo.gz",
"label": "GZ compressed bgeo (.bgeo.gz)"
},
{
"value": "bgeo.lzma",
"label": "LZMA compressed bgeo (.bgeo.lzma)"
},
{
"value": "bgeo.bz2",
"label": "BZip2 compressed bgeo (.bgeo.bz2)"
}
]
return attrs + [
EnumDef("bgeo_type", bgeo_enum, label="BGEO Options"),
]

View file

@ -13,7 +13,8 @@ class CollectFrames(pyblish.api.InstancePlugin):
order = pyblish.api.CollectorOrder + 0.01
label = "Collect Frames"
families = ["vdbcache", "imagesequence", "ass", "redshiftproxy", "review"]
families = ["vdbcache", "imagesequence", "ass",
"redshiftproxy", "review", "bgeo"]
def process(self, instance):
@ -32,9 +33,9 @@ class CollectFrames(pyblish.api.InstancePlugin):
output = output_parm.eval()
_, ext = lib.splitext(
output,
allowed_multidot_extensions=[".ass.gz"]
)
output, allowed_multidot_extensions=[
".ass.gz", ".bgeo.sc", ".bgeo.gz",
".bgeo.lzma", ".bgeo.bz2"])
file_name = os.path.basename(output)
result = file_name
@ -76,7 +77,7 @@ class CollectFrames(pyblish.api.InstancePlugin):
frame = match.group(1)
padding = len(frame)
# Get the parts of the filename surrounding the frame number
# Get the parts of the filename surrounding the frame number,
# so we can put our own frame numbers in.
span = match.span(1)
prefix = match.string[: span[0]]

View file

@ -0,0 +1,21 @@
"""Collector for pointcache types.
This will add additional family to pointcache instance based on
the creator_identifier parameter.
"""
import pyblish.api
class CollectPointcacheType(pyblish.api.InstancePlugin):
"""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.alembic": # noqa: E501
instance.data["families"] += ["abc"]

View file

@ -13,7 +13,7 @@ class ExtractAlembic(publish.Extractor):
order = pyblish.api.ExtractorOrder
label = "Extract Alembic"
hosts = ["houdini"]
families = ["pointcache", "camera"]
families = ["abc", "camera"]
def process(self, instance):

View file

@ -0,0 +1,53 @@
import os
import pyblish.api
from openpype.pipeline import publish
from openpype.hosts.houdini.api.lib import render_rop
from openpype.hosts.houdini.api import lib
import hou
class ExtractBGEO(publish.Extractor):
order = pyblish.api.ExtractorOrder
label = "Extract BGEO"
hosts = ["houdini"]
families = ["bgeo"]
def process(self, instance):
ropnode = hou.node(instance.data["instance_node"])
# Get the filename from the filename parameter
output = ropnode.evalParm("sopoutput")
staging_dir, file_name = os.path.split(output)
instance.data["stagingDir"] = staging_dir
# We run the render
self.log.info("Writing bgeo files '{}' to '{}'.".format(
file_name, staging_dir))
# write files
render_rop(ropnode)
output = instance.data["frames"]
_, ext = lib.splitext(
output[0], allowed_multidot_extensions=[
".ass.gz", ".bgeo.sc", ".bgeo.gz",
".bgeo.lzma", ".bgeo.bz2"])
if "representations" not in instance.data:
instance.data["representations"] = []
representation = {
"name": "bgeo",
"ext": ext.lstrip("."),
"files": output,
"stagingDir": staging_dir,
"frameStart": instance.data["frameStart"],
"frameEnd": instance.data["frameEnd"]
}
instance.data["representations"].append(representation)

View file

@ -17,7 +17,7 @@ class ValidateAbcPrimitiveToDetail(pyblish.api.InstancePlugin):
"""
order = pyblish.api.ValidatorOrder + 0.1
families = ["pointcache"]
families = ["abc"]
hosts = ["houdini"]
label = "Validate Primitive to Detail (Abc)"

View file

@ -18,7 +18,7 @@ class ValidateAlembicROPFaceSets(pyblish.api.InstancePlugin):
"""
order = pyblish.api.ValidatorOrder + 0.1
families = ["pointcache"]
families = ["abc"]
hosts = ["houdini"]
label = "Validate Alembic ROP Face Sets"

View file

@ -14,7 +14,7 @@ class ValidateAlembicInputNode(pyblish.api.InstancePlugin):
"""
order = pyblish.api.ValidatorOrder + 0.1
families = ["pointcache"]
families = ["abc"]
hosts = ["houdini"]
label = "Validate Input Node (Abc)"

View file

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
"""Validator plugin for SOP Path in bgeo isntance."""
import pyblish.api
from openpype.pipeline import PublishValidationError
class ValidateNoSOPPath(pyblish.api.InstancePlugin):
"""Validate if SOP Path in BGEO instance exists."""
order = pyblish.api.ValidatorOrder
families = ["bgeo"]
label = "Validate BGEO SOP Path"
def process(self, instance):
import hou
node = hou.node(instance.data.get("instance_node"))
sop_path = node.evalParm("soppath")
if not sop_path:
raise PublishValidationError(
("Empty SOP Path ('soppath' parameter) found in "
f"the BGEO instance Geometry - {node.path()}"))
if not isinstance(hou.node(sop_path), hou.SopNode):
raise PublishValidationError(
"SOP path is not pointing to valid SOP node.")

View file

@ -19,12 +19,11 @@ class ValidateFileExtension(pyblish.api.InstancePlugin):
"""
order = pyblish.api.ValidatorOrder
families = ["pointcache", "camera", "vdbcache"]
families = ["camera", "vdbcache"]
hosts = ["houdini"]
label = "Output File Extension"
family_extensions = {
"pointcache": ".abc",
"camera": ".abc",
"vdbcache": ".vdb",
}

View file

@ -24,7 +24,7 @@ class ValidatePrimitiveHierarchyPaths(pyblish.api.InstancePlugin):
"""
order = ValidateContentsOrder + 0.1
families = ["pointcache"]
families = ["abc"]
hosts = ["houdini"]
label = "Validate Prims Hierarchy Path"
actions = [AddDefaultPathAction]

View file

@ -132,3 +132,25 @@ switch versions between different hda types.
When you load hda, it will install its type in your hip file and add published version as its definition file. When
you switch version via Scene Manager, it will add its definition and set it as preferred.
## Publishing and loading BGEO caches
There is a simple support for publishing and loading **BGEO** files in all supported compression variants.
### Creating BGEO instances
Select your SOP node to be exported as BGEO. If your selection is in the object level, OpenPype will try to find if there is an `output` node inside, the one with the lowest index will be used:
![BGEO output node](assets/houdini_bgeo_output_node.png)
Then you can open Publisher, in Create you select **BGEO PointCache**:
![BGEO Publisher](assets/houdini_bgeo-publisher.png)
You can select compression type and if the current selection should be connected to ROPs SOP path parameter. Publishing will produce sequence of files based on your timeline settings.
### Loading BGEO
Select your published BGEO subsets in Loader, right click and load them in:
![BGEO Publisher](assets/houdini_bgeo-loading.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB