mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-25 05:14:40 +01:00
Merge pull request #4588 from ynput/feature/OP-3129_houdini-bgeo-publishing
This commit is contained in:
commit
b26b5fdb88
16 changed files with 228 additions and 12 deletions
|
|
@ -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)
|
||||
|
|
|
|||
92
openpype/hosts/houdini/plugins/create/create_bgeo.py
Normal file
92
openpype/hosts/houdini/plugins/create/create_bgeo.py
Normal 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"),
|
||||
]
|
||||
|
|
@ -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]]
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
@ -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):
|
||||
|
||||
|
|
|
|||
53
openpype/hosts/houdini/plugins/publish/extract_bgeo.py
Normal file
53
openpype/hosts/houdini/plugins/publish/extract_bgeo.py
Normal 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)
|
||||
|
|
@ -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)"
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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)"
|
||||
|
||||
|
|
|
|||
|
|
@ -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.")
|
||||
|
|
@ -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",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||

|
||||
|
||||
Then you can open Publisher, in Create you select **BGEO PointCache**:
|
||||
|
||||

|
||||
|
||||
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:
|
||||
|
||||

|
||||
|
|
|
|||
BIN
website/docs/assets/houdini_bgeo-loading.png
Normal file
BIN
website/docs/assets/houdini_bgeo-loading.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 121 KiB |
BIN
website/docs/assets/houdini_bgeo-publisher.png
Normal file
BIN
website/docs/assets/houdini_bgeo-publisher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 92 KiB |
BIN
website/docs/assets/houdini_bgeo_output_node.png
Normal file
BIN
website/docs/assets/houdini_bgeo_output_node.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Loading…
Add table
Add a link
Reference in a new issue