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

Commit

Permalink
Merge pull request #4588 from ynput/feature/OP-3129_houdini-bgeo-publ…
Browse files Browse the repository at this point in the history
…ishing
  • Loading branch information
antirotor authored Jul 17, 2023
2 parents afa1d3c + 99c0031 commit b26b5fd
Show file tree
Hide file tree
Showing 16 changed files with 228 additions and 12 deletions.
2 changes: 2 additions & 0 deletions openpype/hosts/houdini/plugins/create/convert_legacy.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ def convert(self):
"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)
92 changes: 92 additions & 0 deletions openpype/hosts/houdini/plugins/create/create_bgeo.py
Original file line number Diff line number Diff line change
@@ -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"),
]
11 changes: 6 additions & 5 deletions openpype/hosts/houdini/plugins/publish/collect_frames.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):

Expand All @@ -32,9 +33,9 @@ def process(self, instance):
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

Expand Down Expand Up @@ -76,7 +77,7 @@ def create_file_list(match, start_frame, end_frame):
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]]
Expand Down
21 changes: 21 additions & 0 deletions openpype/hosts/houdini/plugins/publish/collect_pointcache_type.py
Original file line number Diff line number Diff line change
@@ -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"]
2 changes: 1 addition & 1 deletion openpype/hosts/houdini/plugins/publish/extract_alembic.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):

Expand Down
53 changes: 53 additions & 0 deletions openpype/hosts/houdini/plugins/publish/extract_bgeo.py
Original file line number Diff line number Diff line change
@@ -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)
Original file line number Diff line number Diff line change
Expand Up @@ -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)"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)"

Expand Down
Original file line number Diff line number Diff line change
@@ -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.")
Original file line number Diff line number Diff line change
Expand Up @@ -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",
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
22 changes: 22 additions & 0 deletions website/docs/artist_hosts_houdini.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 added website/docs/assets/houdini_bgeo-loading.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added website/docs/assets/houdini_bgeo-publisher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added website/docs/assets/houdini_bgeo_output_node.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit b26b5fd

Please sign in to comment.