mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-01 08:24:53 +01:00
work in progress development
This commit is contained in:
parent
55610af68d
commit
7c9581e26b
8 changed files with 334 additions and 0 deletions
21
colorbleed/plugins/fusion/create/create_saver.py
Normal file
21
colorbleed/plugins/fusion/create/create_saver.py
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import avalon.api
|
||||
from avalon import fusion
|
||||
|
||||
|
||||
class CreateTiffSaver(avalon.api.Creator):
|
||||
|
||||
name = "tiffDefault"
|
||||
label = "Create Tiff Saver"
|
||||
hosts = "fusion"
|
||||
family = "colorbleed.imagesequence"
|
||||
|
||||
def process(self):
|
||||
|
||||
comp = fusion.get_current_comp()
|
||||
with fusion.comp_lock_and_undo_chunk(comp):
|
||||
args = (-32768, -32768) # Magical position numbers
|
||||
saver = comp.AddTool("Saver", *args)
|
||||
saver.SetAttrs({
|
||||
"TOOLS_Name": self.data.get("name", self.name),
|
||||
'TOOLST_Clip_FormatID': {1.0: 'TiffFormat'},
|
||||
})
|
||||
27
colorbleed/plugins/fusion/load/load_alembic_camera.py
Normal file
27
colorbleed/plugins/fusion/load/load_alembic_camera.py
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
from avalon import api
|
||||
|
||||
|
||||
class FusionLoadAlembicCamera(api.Loader):
|
||||
"""Load image sequence into Fusion"""
|
||||
|
||||
families = ["colorbleed.camera"]
|
||||
representations = ["ma"]
|
||||
|
||||
label = "Load sequence"
|
||||
order = -10
|
||||
icon = "play-circle"
|
||||
color = "orange"
|
||||
|
||||
def load(self, context, name, namespace, data):
|
||||
""""""
|
||||
|
||||
from avalon.fusion import (imprint_container,
|
||||
get_current_comp,
|
||||
comp_lock_and_undo_chunk)
|
||||
|
||||
current_comp = get_current_comp()
|
||||
with comp_lock_and_undo_chunk(current_comp):
|
||||
tool = current_comp.SurfaceAlembicMesh()
|
||||
tool.SetData("TOOLS_NameSet", )
|
||||
|
||||
pass
|
||||
52
colorbleed/plugins/fusion/publish/_validate_filenames.py
Normal file
52
colorbleed/plugins/fusion/publish/_validate_filenames.py
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
import os
|
||||
|
||||
import pyblish.api
|
||||
|
||||
|
||||
class ValidateFileNames(pyblish.api.Validator):
|
||||
"""Ensure all file names follow the same structure
|
||||
|
||||
Filename should have identifiable parts:
|
||||
- name ( example: Avengers_shot010_preview )
|
||||
- frame ( example: #### )
|
||||
- extension ( example: tiff )
|
||||
|
||||
The result when rendering frame 1250 would be as follows:
|
||||
Avengers_shot010_preview.1250.tiff
|
||||
|
||||
When certain parts need to be rendered out separately for some reason it
|
||||
is advisable to something all the lines of:
|
||||
Avengers_shot010_character_beauty.1250.tiff
|
||||
"""
|
||||
|
||||
order = pyblish.api.ValidatorOrder
|
||||
label = "Validate File Names (Saver)"
|
||||
families = ["colorbleed.imagesequence"]
|
||||
hosts = ["fusion"]
|
||||
|
||||
@classmethod
|
||||
def get_invalid(cls, instance):
|
||||
|
||||
invalid = []
|
||||
|
||||
path = instance.data["path"]
|
||||
basename = os.path.basename(path)
|
||||
|
||||
parts = basename.split(".")
|
||||
if len(parts) != 3:
|
||||
invalid.append(instance)
|
||||
cls.log.error("%s has %i parts, should be 3"
|
||||
% (instance, len(parts)))
|
||||
else:
|
||||
is_numbers = all(i.isdigit() for i in parts[1])
|
||||
if len(parts[1]) != 4 or not is_numbers:
|
||||
cls.log.error("Number padding is not four digits")
|
||||
invalid.append(instance)
|
||||
|
||||
return invalid
|
||||
|
||||
def process(self, instance):
|
||||
invalid = self.get_invalid(instance)
|
||||
if invalid:
|
||||
raise RuntimeError("Found %i instances with a wrong file name "
|
||||
"structure" % len(invalid))
|
||||
19
colorbleed/plugins/fusion/publish/_validate_frame_range.py
Normal file
19
colorbleed/plugins/fusion/publish/_validate_frame_range.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import pyblish.api
|
||||
|
||||
|
||||
class ValidateFrameRange(pyblish.api.InstancePlugin):
|
||||
"""Validate the frame range of the current Saver"""
|
||||
|
||||
order = pyblish.api.ValidatorOrder
|
||||
label = "Validate Frame Range"
|
||||
families = ["colorbleed.imagesequence"]
|
||||
hosts = ["fusion"]
|
||||
|
||||
@classmethod
|
||||
def get_invalid(cls, instance):
|
||||
return []
|
||||
|
||||
def process(self, instance):
|
||||
invalid = self.get_invalid(instance)
|
||||
if invalid:
|
||||
raise RuntimeError("Animation content is invalid. See log.")
|
||||
22
colorbleed/plugins/fusion/publish/collect_comp.py
Normal file
22
colorbleed/plugins/fusion/publish/collect_comp.py
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import pyblish.api
|
||||
|
||||
from avalon import fusion
|
||||
|
||||
|
||||
class CollectCurrentCompFusion(pyblish.api.ContextPlugin):
|
||||
"""Collect current comp"""
|
||||
|
||||
order = pyblish.api.CollectorOrder - 0.4
|
||||
label = "Collect Current Comp"
|
||||
hosts = ["fusion"]
|
||||
|
||||
def process(self, context):
|
||||
"""Collect all image sequence tools"""
|
||||
|
||||
current_comp = fusion.get_current_comp()
|
||||
assert current_comp, "Must have active Fusion composition"
|
||||
context.data["currentComp"] = current_comp
|
||||
|
||||
# Store path to current file
|
||||
attrs = current_comp.GetAttrs()
|
||||
context.data['currentFile'] = attrs.get("COMPS_FileName", "")
|
||||
84
colorbleed/plugins/fusion/publish/collect_instances.py
Normal file
84
colorbleed/plugins/fusion/publish/collect_instances.py
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
import os
|
||||
import re
|
||||
|
||||
import pyblish.api
|
||||
|
||||
|
||||
def get_comp_render_range(comp):
|
||||
"""Return comp's start and end render range."""
|
||||
comp_attrs = comp.GetAttrs()
|
||||
start = comp_attrs["COMPN_RenderStart"]
|
||||
end = comp_attrs["COMPN_RenderEnd"]
|
||||
|
||||
# Whenever render ranges are undefined fall back
|
||||
# to the comp's global start and end
|
||||
if start == -1000000000:
|
||||
start = comp_attrs["COMPN_GlobalEnd"]
|
||||
if end == -1000000000:
|
||||
end = comp_attrs["COMPN_GlobalStart"]
|
||||
|
||||
return start, end
|
||||
|
||||
|
||||
class CollectInstances(pyblish.api.ContextPlugin):
|
||||
"""Collect Fusion saver instances"""
|
||||
|
||||
order = pyblish.api.CollectorOrder
|
||||
label = "Collect Instances"
|
||||
hosts = ["fusion"]
|
||||
|
||||
def process(self, context):
|
||||
"""Collect all image sequence tools"""
|
||||
|
||||
comp = context.data["currentComp"]
|
||||
|
||||
# Get all savers in the comp
|
||||
tools = comp.GetToolList(False).values()
|
||||
savers = [tool for tool in tools if tool.ID == "Saver"]
|
||||
|
||||
start, end = get_comp_render_range(comp)
|
||||
for tool in savers:
|
||||
path = tool["Clip"][comp.TIME_UNDEFINED]
|
||||
|
||||
if not path:
|
||||
self.log.warning("Skipping saver because it "
|
||||
"has no path set: {}".format(tool.Name))
|
||||
continue
|
||||
|
||||
fname = os.path.basename(path)
|
||||
_subset, ext = os.path.splitext(fname)
|
||||
|
||||
# match all digits and character but no points
|
||||
match = re.match("([\d\w]+)", _subset)
|
||||
if not match:
|
||||
self.log.warning("Skipping save because the file name is not"
|
||||
"compatible")
|
||||
subset = match.group(0)
|
||||
|
||||
# Include start and end render frame in label
|
||||
label = "{subset} ({start}-{end})".format(subset=subset,
|
||||
start=int(start),
|
||||
end=int(end))
|
||||
|
||||
instance = context.create_instance(subset)
|
||||
instance.data.update({
|
||||
"asset": os.environ["AVALON_ASSET"], # todo: not a constant
|
||||
"subset": subset,
|
||||
"path": path,
|
||||
"ext": ext, # todo: should be redundant
|
||||
"label": label,
|
||||
"families": ["colorbleed.imagesequence"],
|
||||
"family": "colorbleed.imagesequence",
|
||||
"tool": tool # keep link to the tool
|
||||
})
|
||||
|
||||
self.log.info("Found: \"%s\" " % path)
|
||||
|
||||
# Sort/grouped by family (preserving local index)
|
||||
context[:] = sorted(context, key=self.sort_by_family)
|
||||
|
||||
return context
|
||||
|
||||
def sort_by_family(self, instance):
|
||||
"""Sort by family"""
|
||||
return instance.data.get("families", instance.data.get("family"))
|
||||
80
colorbleed/plugins/fusion/publish/extract_image_sequence.py
Normal file
80
colorbleed/plugins/fusion/publish/extract_image_sequence.py
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import os
|
||||
import glob
|
||||
import re
|
||||
|
||||
import pyblish.api
|
||||
|
||||
_frame_regex = re.compile("[0-9]")
|
||||
|
||||
|
||||
class ExtractImageSequence(pyblish.api.Extractor):
|
||||
"""Extract result of saver by starting a comp render
|
||||
|
||||
This will run the local render of Fusion,
|
||||
"""
|
||||
|
||||
order = pyblish.api.ExtractorOrder
|
||||
label = "Extract Image Sequence"
|
||||
families = ["colorbleed.imagesequence"]
|
||||
hosts = ["fusion"]
|
||||
|
||||
def process(self, context):
|
||||
|
||||
current_comp = context.data["currentComp"]
|
||||
start_frame = current_comp.GetAttrs("COMPN_RenderStart")
|
||||
end_frame = current_comp.GetAttrs("COMPN_RenderEnd")
|
||||
|
||||
# todo: read more about Render table form, page 84
|
||||
# todo: Think out strategy, create renderSettings instance?
|
||||
# Build Fusion Render Job
|
||||
|
||||
self.log.info("Starting render")
|
||||
self.log.info("Start frame: {}".format(start_frame))
|
||||
self.log.info("End frame: {}".format(end_frame))
|
||||
|
||||
result = current_comp.Render()
|
||||
if result:
|
||||
|
||||
# Get all output paths after render was successful
|
||||
# Note the .ID check, this is to ensure we only have savers
|
||||
instances = [i for i in context[:] if i.data["tool"].ID == "Saver"]
|
||||
for instance in instances:
|
||||
# Ensure each instance has its files for the integrator
|
||||
output_path = instance.data["path"]
|
||||
query = self._create_qeury(output_path)
|
||||
files = glob.glob(query)
|
||||
|
||||
if "files" not in instance.data:
|
||||
instance.data["files"] = list()
|
||||
|
||||
print("{} files : {}".format(instance.data["subset"],
|
||||
len(files)))
|
||||
instance.data["files"].append(files)
|
||||
|
||||
# Ensure the integrator has stagingDir
|
||||
instance.data["stagingDir"] = os.path.dirname(output_path)
|
||||
|
||||
def _create_qeury(self, instance):
|
||||
"""Create a queriable string for glob
|
||||
|
||||
Args:
|
||||
instance: instance of current context (comp)
|
||||
|
||||
Returns:
|
||||
str
|
||||
"""
|
||||
|
||||
clipname = instance.data["path"]
|
||||
clip_dir = os.path.dirname(clipname)
|
||||
basename = os.path.basename(clipname)
|
||||
_, ext = os.path.splitext(basename)
|
||||
|
||||
match = re.match("([0-9]{4})", basename)
|
||||
if not match:
|
||||
query_name = "{}.*.{}".format(instance.data["subset"], ext[1:])
|
||||
else:
|
||||
query_name = basename.replace(match.group(0), ".*.")
|
||||
|
||||
query = os.path.join(clip_dir, query_name)
|
||||
|
||||
return query
|
||||
29
colorbleed/plugins/fusion/publish/validate_unique_name.py
Normal file
29
colorbleed/plugins/fusion/publish/validate_unique_name.py
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import pyblish.api
|
||||
|
||||
|
||||
class ValidateUniqueSubsetName(pyblish.api.InstancePlugin):
|
||||
"""Ensure all instances have a unique subset name"""
|
||||
|
||||
order = pyblish.api.ValidatorOrder
|
||||
label = "Validate Unique Subset Names"
|
||||
families = ["colorbleed.imagesequence"]
|
||||
hosts = ["fusion"]
|
||||
|
||||
@classmethod
|
||||
def get_invalid(cls, instance):
|
||||
|
||||
context = instance.context
|
||||
subset = instance.data["subset"]
|
||||
for other_instance in context[:]:
|
||||
if other_instance == instance:
|
||||
continue
|
||||
|
||||
if other_instance.data["subset"] == subset:
|
||||
return [instance] # current instance is invalid
|
||||
|
||||
return []
|
||||
|
||||
def process(self, instance):
|
||||
invalid = self.get_invalid(instance)
|
||||
if invalid:
|
||||
raise RuntimeError("Animation content is invalid. See log.")
|
||||
Loading…
Add table
Add a link
Reference in a new issue