mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-26 22:02:15 +01:00
Merge pull request #169 from BigRoy/feature/OP-4672_Zbrush-integration
Feature/op 4672 zbrush integration
This commit is contained in:
commit
a58e33e3c8
3 changed files with 73 additions and 171 deletions
|
|
@ -3,14 +3,8 @@ import uuid
|
|||
import time
|
||||
import tempfile
|
||||
import logging
|
||||
from ayon_core.client import (
|
||||
get_project,
|
||||
get_asset_by_name,
|
||||
)
|
||||
from ayon_core.pipeline import Anatomy
|
||||
from string import Formatter
|
||||
|
||||
from . import CommunicationWrapper
|
||||
from ayon_core.pipeline.template_data import get_template_data
|
||||
|
||||
|
||||
log = logging.getLogger("zbrush.lib")
|
||||
|
|
@ -46,7 +40,6 @@ def execute_zscript(zscript, communicator=None):
|
|||
|
||||
def execute_zscript_and_wait(zscript,
|
||||
check_filepath=None,
|
||||
sub_level=0,
|
||||
wait=0.1,
|
||||
timeout=20):
|
||||
"""Execute ZScript and wait until ZScript finished processing.
|
||||
|
|
@ -88,81 +81,49 @@ def execute_zscript_and_wait(zscript,
|
|||
)
|
||||
|
||||
|
||||
def find_first_filled_path(path):
|
||||
if not path:
|
||||
return ""
|
||||
|
||||
fields = set()
|
||||
for item in Formatter().parse(path):
|
||||
_, field_name, format_spec, conversion = item
|
||||
if not field_name:
|
||||
continue
|
||||
conversion = "!{}".format(conversion) if conversion else ""
|
||||
format_spec = ":{}".format(format_spec) if format_spec else ""
|
||||
orig_key = "{{{}{}{}}}".format(
|
||||
field_name, conversion, format_spec)
|
||||
fields.add(orig_key)
|
||||
|
||||
for field in fields:
|
||||
path = path.split(field, 1)[0]
|
||||
return path
|
||||
def get_workdir() -> str:
|
||||
"""Return the currently active work directory"""
|
||||
return os.environ["AYON_WORKDIR"]
|
||||
|
||||
|
||||
def get_workdir(project_name, asset_name, task_name):
|
||||
project = get_project(project_name)
|
||||
asset = get_asset_by_name(project_name, asset_name)
|
||||
def export_tool(filepath: str, subdivision_level: int = 0):
|
||||
"""Export active zbrush tool to filepath.
|
||||
|
||||
data = get_template_data(project, asset, task_name)
|
||||
Args:
|
||||
filepath (str): The filepath to export to.
|
||||
subdivision_level (int): The subdivision level to export.
|
||||
A value of zero will export the current subdivision level
|
||||
A negative value, e.g. -1 will go negatively from the highest
|
||||
subdivs - e.g. -1 is the highest available subdiv.
|
||||
|
||||
anatomy = Anatomy(project_name)
|
||||
workdir = anatomy.templates_obj["work"]["folder"].format(data)
|
||||
|
||||
# Remove any potential un-formatted parts of the path
|
||||
valid_workdir = find_first_filled_path(workdir)
|
||||
|
||||
# Path is not filled at all
|
||||
if not valid_workdir:
|
||||
raise AssertionError("Failed to calculate workdir.")
|
||||
|
||||
# Normalize
|
||||
valid_workdir = os.path.normpath(valid_workdir)
|
||||
if os.path.exists(valid_workdir):
|
||||
return valid_workdir
|
||||
|
||||
data.pop("task", None)
|
||||
workdir = anatomy.templates_obj["work"]["folder"].format(data)
|
||||
valid_workdir = find_first_filled_path(workdir)
|
||||
if valid_workdir:
|
||||
# Normalize
|
||||
valid_workdir = os.path.normpath(valid_workdir)
|
||||
if os.path.exists(valid_workdir):
|
||||
return valid_workdir
|
||||
|
||||
|
||||
def export_tool(filepath: str, sub_level: int):
|
||||
"""Export active zbrush tool to filepath."""
|
||||
"""
|
||||
filepath = filepath.replace("\\", "/")
|
||||
export_tool_zscript = ("""
|
||||
[IFreeze,
|
||||
[VarSet, subdlevel, {sub_level}]
|
||||
[VarSet, maxSubd, [IGetMax, Tool:Geometry:SDiv]]
|
||||
[If, subdlevel == 0 || sublevel > maxSubd,
|
||||
[VarSet, subdlevel, maxSubd]]
|
||||
[ISet, "Tool:Geometry:SDiv", subdlevel, 0]
|
||||
|
||||
# Only set any subdiv level if subdiv level != 0
|
||||
set_subdivs_script = ""
|
||||
if subdivision_level != 0:
|
||||
set_subdivs_script = f"""
|
||||
[VarSet, max_subd, [IGetMax, "Tool:Geometry:SDiv"]]
|
||||
[If, max_subd > 0,
|
||||
[ISet, "Tool:Geometry:SDiv", {subdivision_level}, 0],
|
||||
[ISet, "Tool:Geometry:SDiv", [VarGet, max_subd] - {subdivision_level}, 0]
|
||||
]"""
|
||||
|
||||
# Export tool
|
||||
export_tool_zscript = f"""
|
||||
[IFreeze, {set_subdivs_script}
|
||||
[FileNameSetNext, "{filepath}"]
|
||||
[IKeyPress, 13, [IPress, Tool:Export]]
|
||||
]
|
||||
""").format(filepath=filepath, sub_level=sub_level)
|
||||
|
||||
]"""
|
||||
|
||||
# We do not check for the export file's existence because Zbrush might
|
||||
# write the file in chunks, as such the file might exist before the writing
|
||||
# to it has finished
|
||||
execute_zscript_and_wait(
|
||||
export_tool_zscript, check_filepath=filepath,
|
||||
sub_level=sub_level)
|
||||
execute_zscript_and_wait(export_tool_zscript, check_filepath=filepath)
|
||||
|
||||
|
||||
def is_in_edit_mode():
|
||||
def is_in_edit_mode() -> bool:
|
||||
"""Return whether transform edit mode is currently enabled.
|
||||
|
||||
Certain actions can't be performed if Zbrush is currently not within
|
||||
|
|
@ -188,7 +149,7 @@ def is_in_edit_mode():
|
|||
content = str(mode.read())
|
||||
bool_mode = content.rstrip('\x00')
|
||||
|
||||
return bool_mode
|
||||
return bool(int(bool_mode))
|
||||
|
||||
|
||||
def remove_subtool(basename):
|
||||
|
|
|
|||
|
|
@ -53,7 +53,6 @@ class ZbrushHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost):
|
|||
register_event_callback("application.launched", self.initial_app_launch)
|
||||
register_event_callback("application.exit", self.application_exit)
|
||||
|
||||
|
||||
def get_current_project_name(self):
|
||||
"""
|
||||
Returns:
|
||||
|
|
@ -90,19 +89,16 @@ class ZbrushHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost):
|
|||
"folder_path": context.get("folder_path"),
|
||||
"task_name": context.get("task")
|
||||
}
|
||||
|
||||
# --- Workfile ---
|
||||
def open_workfile(self, filepath):
|
||||
open_file_zscript = ("""
|
||||
filepath = filepath.replace("\\", "/")
|
||||
execute_zscript(f"""
|
||||
[IFreeze,
|
||||
[MemCreate, currentfile, 1000, 0]
|
||||
[VarSet, filename, "{filepath}"]
|
||||
[MemWriteString, currentfile, #filename, 0]
|
||||
[FileNameSetNext, #filename]
|
||||
[IKeyPress, 13, [IPress, File:Open:Open]]]
|
||||
[MemDelete, currentfile]
|
||||
[FileNameSetNext, "{filepath}"]
|
||||
[IKeyPress, 13, [IPress, File:Open:Open]]]
|
||||
]
|
||||
""").format(filepath=filepath)
|
||||
execute_zscript(open_file_zscript)
|
||||
""")
|
||||
set_current_file(filepath=filepath)
|
||||
return filepath
|
||||
|
||||
|
|
@ -110,31 +106,23 @@ class ZbrushHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost):
|
|||
if not filepath:
|
||||
filepath = self.get_current_workfile()
|
||||
filepath = filepath.replace("\\", "/")
|
||||
save_file_zscript = ("""
|
||||
[IFreeze,
|
||||
[MemCreate, currentfile, 1000, 0]
|
||||
[VarSet, filename, "{filepath}"]
|
||||
[MemWriteString, currentfile, #filename, 0]
|
||||
[FileNameSetNext, #filename]
|
||||
[IKeyPress, 13, [IPress, File:SaveAs:SaveAs]]]
|
||||
[MemDelete, currentfile]
|
||||
]
|
||||
""").format(filepath=filepath)
|
||||
# # move the json data to the files
|
||||
# # shutil.copy
|
||||
copy_ayon_data(filepath)
|
||||
set_current_file(filepath=filepath)
|
||||
execute_zscript(save_file_zscript)
|
||||
execute_zscript(f"""
|
||||
[IFreeze,
|
||||
[FileNameSetNext, "{filepath}"]
|
||||
[IKeyPress, 13, [IPress, File:SaveAs:SaveAs]]]
|
||||
]
|
||||
""")
|
||||
return filepath
|
||||
|
||||
def work_root(self, session):
|
||||
return session["AYON_WORKDIR"]
|
||||
|
||||
def get_current_workfile(self):
|
||||
project_name = get_current_context()["project_name"]
|
||||
folder_path = get_current_context()["folder_path"]
|
||||
task_name = get_current_context()["task_name"]
|
||||
work_dir = get_workdir(project_name, folder_path, task_name)
|
||||
work_dir = get_workdir()
|
||||
txt_dir = os.path.join(
|
||||
work_dir, ".zbrush_metadata").replace(
|
||||
"\\", "/"
|
||||
|
|
@ -194,6 +182,7 @@ class ZbrushHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost):
|
|||
def get_context_data(self):
|
||||
get_load_workfile_metadata(ZBRUSH_METADATA_CREATE_CONTEXT)
|
||||
|
||||
|
||||
def containerise(
|
||||
name, context, namespace="", loader=None, containers=None):
|
||||
data = {
|
||||
|
|
@ -218,10 +207,7 @@ def save_current_workfile_context(context):
|
|||
|
||||
|
||||
def write_context_metadata(metadata_key, context):
|
||||
project_name = get_current_context()["project_name"]
|
||||
folder_path = get_current_context()["folder_path"]
|
||||
task_name = get_current_context()["task_name"]
|
||||
work_dir = get_workdir(project_name, folder_path, task_name)
|
||||
work_dir = get_workdir()
|
||||
json_dir = os.path.join(
|
||||
work_dir, ".zbrush_metadata", metadata_key).replace(
|
||||
"\\", "/"
|
||||
|
|
@ -242,14 +228,11 @@ def write_context_metadata(metadata_key, context):
|
|||
def write_workfile_metadata(metadata_key, data=None):
|
||||
if data is None:
|
||||
data = []
|
||||
project_name = get_current_context()["project_name"]
|
||||
folder_path = get_current_context()["folder_path"]
|
||||
task_name = get_current_context()["task_name"]
|
||||
current_file = registered_host().get_current_workfile()
|
||||
if current_file:
|
||||
current_file = os.path.splitext(
|
||||
os.path.basename(current_file))[0].strip()
|
||||
work_dir = get_workdir(project_name, folder_path, task_name)
|
||||
work_dir = get_workdir()
|
||||
json_dir = os.path.join(
|
||||
work_dir, ".zbrush_metadata",
|
||||
current_file, metadata_key).replace(
|
||||
|
|
@ -274,31 +257,6 @@ def get_current_context():
|
|||
}
|
||||
|
||||
|
||||
def get_workfile_metadata(metadata_key, default=None):
|
||||
if default is None:
|
||||
default = []
|
||||
output_file = tempfile.NamedTemporaryFile(
|
||||
mode="w", prefix="a_zb_meta", suffix=".txt", delete=False
|
||||
)
|
||||
output_file.close()
|
||||
output_filepath = output_file.name.replace("\\", "/")
|
||||
context_data_zscript = ("""
|
||||
[IFreeze,
|
||||
[If, [MemCreate, {metadata_key}, 400000, 0] !=-1,
|
||||
[MemCreate, {metadata_key}, 400000, 0]
|
||||
[MemWriteString, {metadata_key}, "{default}", 0]]
|
||||
[MemSaveToFile, {metadata_key}, "{output_filepath}", 1]
|
||||
[MemDelete, {metadata_key}]
|
||||
]
|
||||
""").format(metadata_key=metadata_key,
|
||||
default=default, output_filepath=output_filepath)
|
||||
execute_zscript(context_data_zscript)
|
||||
with open(output_filepath) as data:
|
||||
file_content = str(data.read().strip()).rstrip('\x00')
|
||||
file_content = ast.literal_eval(file_content)
|
||||
return file_content
|
||||
|
||||
|
||||
def get_containers():
|
||||
output = get_load_workfile_metadata(ZBRUSH_SECTION_NAME_CONTAINERS)
|
||||
if output:
|
||||
|
|
@ -311,16 +269,14 @@ def get_containers():
|
|||
|
||||
return output
|
||||
|
||||
|
||||
def write_load_metadata(metadata_key, data):
|
||||
#TODO: create temp json file
|
||||
project_name = get_current_context()["project_name"]
|
||||
folder_path = get_current_context()["folder_path"]
|
||||
task_name = get_current_context()["task_name"]
|
||||
current_file = registered_host().get_current_workfile()
|
||||
if current_file:
|
||||
current_file = os.path.splitext(
|
||||
os.path.basename(current_file))[0].strip()
|
||||
work_dir = get_workdir(project_name, folder_path, task_name)
|
||||
work_dir = get_workdir()
|
||||
name = next((d["name"] for d in data), None)
|
||||
json_dir = os.path.join(
|
||||
work_dir, ".zbrush_metadata",
|
||||
|
|
@ -339,10 +295,7 @@ def write_load_metadata(metadata_key, data):
|
|||
|
||||
def get_load_context_metadata(metadata_key):
|
||||
file_content = []
|
||||
project_name = get_current_context()["project_name"]
|
||||
folder_path = get_current_context()["folder_path"]
|
||||
task_name = get_current_context()["task_name"]
|
||||
work_dir = get_workdir(project_name, folder_path, task_name)
|
||||
work_dir = get_workdir()
|
||||
json_dir = os.path.join(
|
||||
work_dir, ".zbrush_metadata", metadata_key).replace(
|
||||
"\\", "/"
|
||||
|
|
@ -363,14 +316,11 @@ def get_load_workfile_metadata(metadata_key):
|
|||
# save zscript to the hidden folder
|
||||
# load json files
|
||||
file_content = []
|
||||
project_name = get_current_context()["project_name"]
|
||||
folder_path = get_current_context()["folder_path"]
|
||||
task_name = get_current_context()["task_name"]
|
||||
current_file = registered_host().get_current_workfile()
|
||||
if current_file:
|
||||
current_file = os.path.splitext(
|
||||
os.path.basename(current_file))[0].strip()
|
||||
work_dir = get_workdir(project_name, folder_path, task_name)
|
||||
work_dir = get_workdir()
|
||||
json_dir = os.path.join(
|
||||
work_dir, ".zbrush_metadata",
|
||||
current_file, metadata_key).replace(
|
||||
|
|
@ -393,14 +343,11 @@ def get_instance_workfile_metadata(metadata_key):
|
|||
# save zscript to the hidden folder
|
||||
# load json files
|
||||
file_content = []
|
||||
project_name = get_current_context()["project_name"]
|
||||
folder_path = get_current_context()["folder_path"]
|
||||
task_name = get_current_context()["task_name"]
|
||||
current_file = registered_host().get_current_workfile()
|
||||
if current_file:
|
||||
current_file = os.path.splitext(
|
||||
os.path.basename(current_file))[0].strip()
|
||||
work_dir = get_workdir(project_name, folder_path, task_name)
|
||||
work_dir = get_workdir()
|
||||
json_dir = os.path.join(
|
||||
work_dir, ".zbrush_metadata",
|
||||
current_file, metadata_key).replace(
|
||||
|
|
@ -416,14 +363,11 @@ def get_instance_workfile_metadata(metadata_key):
|
|||
|
||||
|
||||
def remove_container_data(name):
|
||||
project_name = get_current_context()["project_name"]
|
||||
folder_path = get_current_context()["folder_path"]
|
||||
task_name = get_current_context()["task_name"]
|
||||
current_file = registered_host().get_current_workfile()
|
||||
if current_file:
|
||||
current_file = os.path.splitext(
|
||||
os.path.basename(current_file))[0].strip()
|
||||
work_dir = get_workdir(project_name, folder_path, task_name)
|
||||
work_dir = get_workdir()
|
||||
json_dir = os.path.join(
|
||||
work_dir, ".zbrush_metadata",
|
||||
current_file, ZBRUSH_SECTION_NAME_CONTAINERS).replace(
|
||||
|
|
@ -437,10 +381,7 @@ def remove_container_data(name):
|
|||
|
||||
|
||||
def remove_tmp_data():
|
||||
project_name = get_current_context()["project_name"]
|
||||
folder_path = get_current_context()["folder_path"]
|
||||
task_name = get_current_context()["task_name"]
|
||||
work_dir = get_workdir(project_name, folder_path, task_name)
|
||||
work_dir = get_workdir()
|
||||
for name in [ZBRUSH_METADATA_CREATE_CONTEXT,
|
||||
ZBRUSH_SECTION_NAME_INSTANCES,
|
||||
ZBRUSH_SECTION_NAME_CONTAINERS]:
|
||||
|
|
@ -458,14 +399,11 @@ def remove_tmp_data():
|
|||
|
||||
def copy_ayon_data(filepath):
|
||||
filename = os.path.splitext(os.path.basename(filepath))[0].strip()
|
||||
project_name = get_current_context()["project_name"]
|
||||
folder_path = get_current_context()["folder_path"]
|
||||
task_name = get_current_context()["task_name"]
|
||||
current_file = registered_host().get_current_workfile()
|
||||
if current_file:
|
||||
current_file = os.path.splitext(
|
||||
os.path.basename(current_file))[0].strip()
|
||||
work_dir = get_workdir(project_name, folder_path, task_name)
|
||||
work_dir = get_workdir()
|
||||
for name in [ZBRUSH_METADATA_CREATE_CONTEXT,
|
||||
ZBRUSH_SECTION_NAME_INSTANCES,
|
||||
ZBRUSH_SECTION_NAME_CONTAINERS]:
|
||||
|
|
@ -490,10 +428,7 @@ def copy_ayon_data(filepath):
|
|||
|
||||
|
||||
def set_current_file(filepath=None):
|
||||
project_name = get_current_context()["project_name"]
|
||||
folder_path = get_current_context()["folder_path"]
|
||||
task_name = get_current_context()["task_name"]
|
||||
work_dir = get_workdir(project_name, folder_path, task_name)
|
||||
work_dir = get_workdir()
|
||||
txt_dir = os.path.join(
|
||||
work_dir, ".zbrush_metadata").replace(
|
||||
"\\", "/"
|
||||
|
|
@ -516,14 +451,11 @@ def imprint(container, representation_id):
|
|||
old_container_data = []
|
||||
data = {}
|
||||
name = container["objectName"]
|
||||
project_name = get_current_context()["project_name"]
|
||||
folder_path = get_current_context()["folder_path"]
|
||||
task_name = get_current_context()["task_name"]
|
||||
current_file = registered_host().get_current_workfile()
|
||||
if current_file:
|
||||
current_file = os.path.splitext(
|
||||
os.path.basename(current_file))[0].strip()
|
||||
work_dir = get_workdir(project_name, folder_path, task_name)
|
||||
work_dir = get_workdir()
|
||||
json_dir = os.path.join(
|
||||
work_dir, ".zbrush_metadata",
|
||||
current_file, ZBRUSH_SECTION_NAME_CONTAINERS).replace(
|
||||
|
|
@ -546,6 +478,7 @@ def imprint(container, representation_id):
|
|||
file.write(new_container_data)
|
||||
file.close()
|
||||
|
||||
|
||||
def tmp_current_file_check():
|
||||
output_file = tempfile.NamedTemporaryFile(
|
||||
mode="w", prefix="a_zb_cfc", suffix=".txt", delete=False
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ class CreateModel(plugin.ZbrushCreator):
|
|||
identifier = "io.ayon.creators.zbrush.model"
|
||||
label = "Model"
|
||||
product_type = "model"
|
||||
icon = "gear"
|
||||
icon = "cube"
|
||||
|
||||
def create(self, product_name, instance_data, pre_create_data):
|
||||
creator_attributes = instance_data.setdefault(
|
||||
|
|
@ -25,11 +25,19 @@ class CreateModel(plugin.ZbrushCreator):
|
|||
|
||||
def get_instance_attr_defs(self):
|
||||
return [
|
||||
NumberDef("subd_level",
|
||||
label="Subdivision Level",
|
||||
decimals=0,
|
||||
minimum=0,
|
||||
default=0)
|
||||
NumberDef(
|
||||
"subd_level",
|
||||
label="Subdivision Level",
|
||||
decimals=0,
|
||||
minimum=-10,
|
||||
default=0,
|
||||
tooltip=(
|
||||
"A level of 1 or higher sets the level to export at.\n"
|
||||
"A level of 0 means 'Use tool's current subdiv level'.\n"
|
||||
"A level of -1 or lower subtracts from the highest subdiv,"
|
||||
"\n for example -1 means highest subdiv level."
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
def get_pre_create_attr_defs(self):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue