mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Introduce houdini unreal static mesh
This commit is contained in:
parent
10a539e65d
commit
e1f2a77089
7 changed files with 169 additions and 28 deletions
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
],
|
||||
"ext": ".ass"
|
||||
},
|
||||
"HouCreateUnrealStaticMesh": {
|
||||
"CreateUnrealStaticMesh": {
|
||||
"enabled": true,
|
||||
"default_variants": [
|
||||
"Main"
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@
|
|||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "HouCreateUnrealStaticMesh",
|
||||
"key": "CreateUnrealStaticMesh",
|
||||
"label": "Create Unreal - Static Mesh",
|
||||
"checkbox_key": "enabled",
|
||||
"children": [
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue