-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'develop' into bugfix/add_deadline_to_prerender
- Loading branch information
Showing
95 changed files
with
600 additions
and
104 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
30 changes: 30 additions & 0 deletions
30
client/ayon_core/hosts/blender/plugins/create/create_usd.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
"""Create a USD Export.""" | ||
|
||
from ayon_core.hosts.blender.api import plugin, lib | ||
|
||
|
||
class CreateUSD(plugin.BaseCreator): | ||
"""Create USD Export""" | ||
|
||
identifier = "io.openpype.creators.blender.usd" | ||
name = "usdMain" | ||
label = "USD" | ||
product_type = "usd" | ||
icon = "gears" | ||
|
||
def create( | ||
self, product_name: str, instance_data: dict, pre_create_data: dict | ||
): | ||
# Run parent create method | ||
collection = super().create( | ||
product_name, instance_data, pre_create_data | ||
) | ||
|
||
if pre_create_data.get("use_selection"): | ||
objects = lib.get_selection() | ||
for obj in objects: | ||
collection.objects.link(obj) | ||
if obj.type == 'EMPTY': | ||
objects.extend(obj.children) | ||
|
||
return collection |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
90 changes: 90 additions & 0 deletions
90
client/ayon_core/hosts/blender/plugins/publish/extract_usd.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import os | ||
|
||
import bpy | ||
|
||
from ayon_core.pipeline import publish | ||
from ayon_core.hosts.blender.api import plugin, lib | ||
|
||
|
||
class ExtractUSD(publish.Extractor): | ||
"""Extract as USD.""" | ||
|
||
label = "Extract USD" | ||
hosts = ["blender"] | ||
families = ["usd"] | ||
|
||
def process(self, instance): | ||
|
||
# Ignore runtime instances (e.g. USD layers) | ||
# TODO: This is better done via more specific `families` | ||
if not instance.data.get("transientData", {}).get("instance_node"): | ||
return | ||
|
||
# Define extract output file path | ||
stagingdir = self.staging_dir(instance) | ||
filename = f"{instance.name}.usd" | ||
filepath = os.path.join(stagingdir, filename) | ||
|
||
# Perform extraction | ||
self.log.debug("Performing extraction..") | ||
|
||
# Select all members to "export selected" | ||
plugin.deselect_all() | ||
|
||
selected = [] | ||
for obj in instance: | ||
if isinstance(obj, bpy.types.Object): | ||
obj.select_set(True) | ||
selected.append(obj) | ||
|
||
root = lib.get_highest_root(objects=instance[:]) | ||
if not root: | ||
instance_node = instance.data["transientData"]["instance_node"] | ||
raise publish.KnownPublishError( | ||
f"No root object found in instance: {instance_node.name}" | ||
) | ||
self.log.debug(f"Exporting using active root: {root.name}") | ||
|
||
context = plugin.create_blender_context( | ||
active=root, selected=selected) | ||
|
||
# Export USD | ||
with bpy.context.temp_override(**context): | ||
bpy.ops.wm.usd_export( | ||
filepath=filepath, | ||
selected_objects_only=True, | ||
export_textures=False, | ||
relative_paths=False, | ||
export_animation=False, | ||
export_hair=False, | ||
export_uvmaps=True, | ||
# TODO: add for new version of Blender (4+?) | ||
# export_mesh_colors=True, | ||
export_normals=True, | ||
export_materials=True, | ||
use_instancing=True | ||
) | ||
|
||
plugin.deselect_all() | ||
|
||
# Add representation | ||
representation = { | ||
'name': 'usd', | ||
'ext': 'usd', | ||
'files': filename, | ||
"stagingDir": stagingdir, | ||
} | ||
instance.data.setdefault("representations", []).append(representation) | ||
self.log.debug("Extracted instance '%s' to: %s", | ||
instance.name, representation) | ||
|
||
|
||
class ExtractModelUSD(ExtractUSD): | ||
"""Extract model as USD.""" | ||
|
||
label = "Extract USD (Model)" | ||
hosts = ["blender"] | ||
families = ["model"] | ||
|
||
# Driven by settings | ||
optional = True |
141 changes: 141 additions & 0 deletions
141
client/ayon_core/hosts/houdini/plugins/create/create_model.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
# -*- coding: utf-8 -*- | ||
"""Creator plugin for creating Model product type. | ||
Note: | ||
Currently, This creator plugin is the same as 'create_pointcache.py' | ||
But renaming the product type to 'model'. | ||
It's purpose to support | ||
Maya (load/publish model from maya to/from houdini). | ||
It's considered to support multiple representations in the future. | ||
""" | ||
|
||
from ayon_core.hosts.houdini.api import plugin | ||
from ayon_core.lib import BoolDef | ||
|
||
import hou | ||
|
||
|
||
|
||
class CreateModel(plugin.HoudiniCreator): | ||
"""Create Model""" | ||
identifier = "io.openpype.creators.houdini.model" | ||
label = "Model" | ||
product_type = "model" | ||
icon = "cube" | ||
|
||
def create(self, product_name, instance_data, pre_create_data): | ||
instance_data.pop("active", None) | ||
instance_data.update({"node_type": "alembic"}) | ||
creator_attributes = instance_data.setdefault( | ||
"creator_attributes", dict()) | ||
creator_attributes["farm"] = pre_create_data["farm"] | ||
|
||
instance = super(CreateModel, self).create( | ||
product_name, | ||
instance_data, | ||
pre_create_data) | ||
|
||
instance_node = hou.node(instance.get("instance_node")) | ||
parms = { | ||
"use_sop_path": True, | ||
"build_from_path": True, | ||
"path_attrib": "path", | ||
"prim_to_detail_pattern": "cbId", | ||
"format": 2, | ||
"facesets": 0, | ||
"filename": hou.text.expandString( | ||
"$HIP/pyblish/{}.abc".format(product_name)) | ||
} | ||
|
||
if self.selected_nodes: | ||
selected_node = self.selected_nodes[0] | ||
|
||
# Although Houdini allows ObjNode path on `sop_path` for the | ||
# the ROP node we prefer it set to the SopNode path explicitly | ||
|
||
# Allow sop level paths (e.g. /obj/geo1/box1) | ||
if isinstance(selected_node, hou.SopNode): | ||
parms["sop_path"] = selected_node.path() | ||
self.log.debug( | ||
"Valid SopNode selection, 'SOP Path' in ROP will be set to '%s'." | ||
% selected_node.path() | ||
) | ||
|
||
# Allow object level paths to Geometry nodes (e.g. /obj/geo1) | ||
# but do not allow other object level nodes types like cameras, etc. | ||
elif isinstance(selected_node, hou.ObjNode) and \ | ||
selected_node.type().name() in ["geo"]: | ||
|
||
# get the output node with the minimum | ||
# 'outputidx' or the node with display flag | ||
sop_path = self.get_obj_output(selected_node) | ||
|
||
if sop_path: | ||
parms["sop_path"] = sop_path.path() | ||
self.log.debug( | ||
"Valid ObjNode selection, 'SOP Path' in ROP will be set to " | ||
"the child path '%s'." | ||
% sop_path.path() | ||
) | ||
|
||
if not parms.get("sop_path", None): | ||
self.log.debug( | ||
"Selection isn't valid. 'SOP Path' in ROP will be empty." | ||
) | ||
else: | ||
self.log.debug( | ||
"No Selection. 'SOP Path' in ROP will be empty." | ||
) | ||
|
||
instance_node.setParms(parms) | ||
instance_node.parm("trange").set(1) | ||
|
||
# Explicitly set f1 and f2 to frame start. | ||
# Which forces the rop node to export one frame. | ||
instance_node.parmTuple('f').deleteAllKeyframes() | ||
fstart = int(hou.hscriptExpression("$FSTART")) | ||
instance_node.parmTuple('f').set((fstart, fstart, 1)) | ||
|
||
# Lock any parameters in this list | ||
to_lock = ["prim_to_detail_pattern"] | ||
self.lock_parameters(instance_node, to_lock) | ||
|
||
def get_network_categories(self): | ||
return [ | ||
hou.ropNodeTypeCategory(), | ||
hou.sopNodeTypeCategory() | ||
] | ||
|
||
def get_obj_output(self, obj_node): | ||
"""Find output node with the smallest 'outputidx'.""" | ||
|
||
outputs = obj_node.subnetOutputs() | ||
|
||
# if obj_node is empty | ||
if not outputs: | ||
return | ||
|
||
# if obj_node has one output child whether its | ||
# sop output node or a node with the render flag | ||
elif len(outputs) == 1: | ||
return outputs[0] | ||
|
||
# if there are more than one, then it have multiple output nodes | ||
# return the one with the minimum 'outputidx' | ||
else: | ||
return min(outputs, | ||
key=lambda node: node.evalParm('outputidx')) | ||
|
||
def get_instance_attr_defs(self): | ||
return [ | ||
BoolDef("farm", | ||
label="Submitting to Farm", | ||
default=False) | ||
] | ||
|
||
def get_pre_create_attr_defs(self): | ||
attrs = super().get_pre_create_attr_defs() | ||
# Use same attributes as for instance attributes | ||
return attrs + self.get_instance_attr_defs() |
Oops, something went wrong.