Introduce houdini unreal static mesh

This commit is contained in:
Mustafa-Zarkash 2023-08-25 22:36:38 +03:00
parent 10a539e65d
commit e1f2a77089
7 changed files with 169 additions and 28 deletions

View file

@ -1,13 +1,15 @@
# -*- coding: utf-8 -*-
"""Creator plugin for creating fbx.
"""Creator plugin for creating Unreal Static Meshes.
It was made to pratice publish process.
Unreal Static Meshes will be published as FBX.
Filmbox by default expects an ObjNode
however, we set the sop node explictly
to eleminate any confusion.
This creator by default will select
This will make Filmbox to ignore any object transformations!
get_obj_output selects
the output sop with mimimum idx
or the node with render flag isntead.
@ -15,13 +17,13 @@ This plugin is part of publish process guide.
"""
from openpype.hosts.houdini.api import plugin
from openpype.lib import EnumDef
from openpype.lib import BoolDef, EnumDef
import hou
class HouCreateUnrealStaticMesh(plugin.HoudiniCreator):
"""Filmbox FBX Driver."""
class CreateUnrealStaticMesh(plugin.HoudiniCreator):
"""Unreal Static Meshes with collisions. """
# you should set
identifier = "io.openpype.creators.houdini.unrealstaticmesh"
@ -41,7 +43,7 @@ class HouCreateUnrealStaticMesh(plugin.HoudiniCreator):
instance_data.update({"node_type": "filmboxfbx"})
# create instance (calls HoudiniCreator.create())
instance = super(HouCreateUnrealStaticMesh, self).create(
instance = super(CreateUnrealStaticMesh, self).create(
subset_name,
instance_data,
pre_create_data)
@ -78,8 +80,15 @@ class HouCreateUnrealStaticMesh(plugin.HoudiniCreator):
},
default=0,
label="Vertex Cache Format")
convert_units = BoolDef("convertunits",
tooltip="When on, the FBX is converted"
"from the current Houdini "
"system units to the native "
"FBX unit of centimeters.",
default=False,
label="Convert Units")
return attrs + [vcformat]
return attrs + [vcformat, convert_units]
def get_parms(self, subset_name, pre_create_data):
"""Get parameters values for this specific node."""
@ -94,15 +103,18 @@ class HouCreateUnrealStaticMesh(plugin.HoudiniCreator):
# 3. get Vertex Cache Format
vcformat = pre_create_data.get("vcformat")
# 4. Valid Frame Range
# It should publish the current frame.
trange = 0
# 4. get convert_units
convertunits = pre_create_data.get("convertunits")
# 5. get Valid Frame Range
trange = 1
# parms dictionary
parms = {
"startnode": selection,
"sopoutput": output_path,
"vcformat": vcformat,
"convertunits": convertunits,
"trange": trange
}
@ -166,7 +178,7 @@ class HouCreateUnrealStaticMesh(plugin.HoudiniCreator):
def get_obj_output(self, obj_node):
"""Find output node with the smallest 'outputidx'
or return tje node with the render flag instead.
or return the node with the render flag instead.
"""
outputs = obj_node.subnetOutputs()

View file

@ -16,7 +16,7 @@ from openpype.hosts.houdini.api.lib import render_rop
import hou
class ExtractRedshiftProxy(publish.Extractor):
class ExtractFBX(publish.Extractor):
label = "Extract FBX"
families = ["fbx"]

View file

@ -65,8 +65,8 @@ class ValidateFBXPrimitiveHierarchyPaths(pyblish.api.InstancePlugin,
actions = [SelectInvalidAction, AddDefaultPathAction,
SelectROPAction]
# 'OptionalPyblishPluginMixin' where logic for 'optional' is implemented.
# It requires updating project settings
# 'OptionalPyblishPluginMixin' adds the functionality to
# enable/disable plugins, It requires adding new settings.
optional = True
# overrides InstancePlugin.process()
@ -116,13 +116,9 @@ class ValidateFBXPrimitiveHierarchyPaths(pyblish.api.InstancePlugin,
cls.log.debug("Checking for attribute: %s", path_attr)
# Get frame
frame = hou.intFrame()
trange = rop_node.parm("trange").eval()
if trange:
frame = int(hou.playbar.frameRange()[0])
frame = instance.data.get("frameStart", frame)
# Use current frame if "frameStart" doesn't exist
# This only happens when ""trange" is 0
frame = instance.data.get("frameStart", hou.intFrame())
# Get Geo at that frame
geo = output_node.geometryAtFrame(frame)

View file

@ -0,0 +1,133 @@
# -*- coding: utf-8 -*-
"""Validate mesh is static.
This plugin is part of publish process guide.
"""
import pyblish.api
from openpype.pipeline import PublishValidationError
from openpype.pipeline.publish import (
ValidateContentsOrder,
RepairAction,
)
from openpype.hosts.houdini.api.action import (
SelectInvalidAction,
SelectROPAction,
)
import hou
# Each validation can have a single repair action
# which calls the repair method
class FreezeTimeAction(RepairAction):
label = "Freeze Time"
icon = "ei.pause-alt"
class ValidateMeshIsStatic(pyblish.api.InstancePlugin):
"""Validate mesh is static.
It checks if node is time dependant.
"""
families = ["staticMesh"]
hosts = ["houdini"]
label = "Validate mesh is static"
# Usually you will use this value as default
order = ValidateContentsOrder + 0.1
# Validation can have as many actions as you want
# all of these actions are defined in a seperate place
# unlike the repair action
actions = [FreezeTimeAction, SelectInvalidAction,
SelectROPAction]
# overrides InstancePlugin.process()
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),
title=self.label
)
# This method was named get_invalid as a convention
# it's also used by SelectInvalidAction to select
# the returned nodes
@classmethod
def get_invalid(cls, instance):
output_node = instance.data.get("output_node")
if output_node.isTimeDependent():
cls.log.info("Mesh is not static!")
return [output_node]
# what repair action expects to find and call
@classmethod
def repair(cls, instance):
"""Adds a time shift node.
It should kill time dependency.
"""
rop_node = hou.node(instance.data["instance_node"])
# I'm doing so because an artist may change output node
# before clicking the button.
output_node = rop_node.parm("startnode").evalAsNode()
if not output_node:
cls.log.debug(
"Action isn't performed, invalid SOP Path on %s",
rop_node
)
return
# This check to prevent the action from running multiple times.
# git_invalid only returns [output_node] when
# path attribute is the problem
if cls.get_invalid(instance) != [output_node]:
return
time_shift = output_node.parent().createNode("timeshift",
"freeze_time")
time_shift.parm("frame").deleteAllKeyframes()
frame = instance.data.get("frameStart", hou.intFrame())
time_shift.parm("frame").set(frame)
cls.log.debug(
"'%s' was created. It will kill time dependency."
, time_shift
)
time_shift.setGenericFlag(hou.nodeFlag.DisplayComment, True)
time_shift.setComment(
'This node was created automatically by '
'"Freeze Time" Action'
'\nFeel free to modify or replace it.'
)
if output_node.type().name() in ["null", "output"]:
# Connect before
time_shift.setFirstInput(output_node.input(0))
time_shift.moveToGoodPosition()
output_node.setFirstInput(time_shift)
output_node.moveToGoodPosition()
else:
# Connect after
time_shift.setFirstInput(output_node)
rop_node.parm("startnode").set(time_shift.path())
time_shift.moveToGoodPosition()
cls.log.debug(
"SOP path on '%s' updated to new output node '%s'",
rop_node, time_shift
)

View file

@ -19,7 +19,7 @@
],
"ext": ".ass"
},
"HouCreateUnrealStaticMesh": {
"CreateUnrealStaticMesh": {
"enabled": true,
"default_variants": [
"Main"

View file

@ -42,7 +42,7 @@
{
"type": "dict",
"collapsible": true,
"key": "HouCreateUnrealStaticMesh",
"key": "CreateUnrealStaticMesh",
"label": "Create Unreal - Static Mesh",
"checkbox_key": "enabled",
"children": [

View file

@ -21,7 +21,7 @@ class CreateArnoldAssModel(BaseSettingsModel):
ext: str = Field(Title="Extension")
class HouCreateUnrealStaticMeshModel(BaseSettingsModel):
class CreateUnrealStaticMeshModel(BaseSettingsModel):
enabled: bool = Field(title="Enabled")
default_variants: list[str] = Field(
default_factory=list,
@ -39,8 +39,8 @@ class CreatePluginsModel(BaseSettingsModel):
default_factory=CreateArnoldAssModel,
title="Create Alembic Camera")
# "-" is not compatible in the new model
HouCreateUnrealStaticMesh: HouCreateUnrealStaticMeshModel = Field(
default_factory=HouCreateUnrealStaticMeshModel,
CreateUnrealStaticMesh: CreateUnrealStaticMeshModel = Field(
default_factory=CreateUnrealStaticMeshModel,
title="Create Unreal_Static Mesh"
)
CreateAlembicCamera: CreatorModel = Field(
@ -81,7 +81,7 @@ DEFAULT_HOUDINI_CREATE_SETTINGS = {
"default_variants": ["Main"],
"ext": ".ass"
},
"HouCreateUnrealStaticMesh": {
"CreateUnrealStaticMesh": {
"enabled": True,
"default_variants": [
"Main"