Refactor metadata code to allow more structure for future Substance Painter plugins

This commit is contained in:
Roy Nieterau 2023-01-07 17:42:43 +01:00
parent c101f6a2cb
commit ccb4371641
3 changed files with 91 additions and 48 deletions

View file

@ -36,6 +36,10 @@ LOAD_PATH = os.path.join(PLUGINS_DIR, "load")
CREATE_PATH = os.path.join(PLUGINS_DIR, "create")
INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory")
OPENPYPE_METADATA_KEY = "OpenPype"
OPENPYPE_METADATA_CONTAINERS_KEY = "containers" # child key
self = sys.modules[__name__]
self.menu = None
self.callbacks = []
@ -127,8 +131,8 @@ class SubstanceHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost):
if not substance_painter.project.is_open():
return
metadata = substance_painter.project.Metadata("OpenPype")
containers = metadata.get("containers")
metadata = substance_painter.project.Metadata(OPENPYPE_METADATA_KEY)
containers = metadata.get(OPENPYPE_METADATA_CONTAINERS_KEY)
if containers:
for key, container in containers.items():
container["objectName"] = key
@ -275,3 +279,49 @@ def imprint_container(container,
]
for key, value in data:
container[key] = value
def set_project_metadata(key, data):
"""Set a key in project's OpenPype metadata."""
metadata = substance_painter.project.Metadata(OPENPYPE_METADATA_KEY)
metadata.set(key, data)
def get_project_metadata(key):
"""Get a key from project's OpenPype metadata."""
metadata = substance_painter.project.Metadata(OPENPYPE_METADATA_KEY)
return metadata.get(key)
def set_container_metadata(object_name, container_data, update=False):
"""Helper method to directly set the data for a specific container
Args:
object_name (str): The unique object name identifier for the container
container_data (dict): The data for the container.
Note 'objectName' data is derived from `object_name` and key in
`container_data` will be ignored.
update (bool): Whether to only update the dict data.
"""
# The objectName is derived from the key in the metadata so won't be stored
# in the metadata in the container's data.
container_data.pop("objectName", None)
metadata = substance_painter.project.Metadata(OPENPYPE_METADATA_KEY)
containers = metadata.get(OPENPYPE_METADATA_CONTAINERS_KEY) or {}
if update:
existing_data = containers.setdefault(object_name, {})
existing_data.update(container_data) # mutable dict, in-place update
else:
containers[object_name] = container_data
metadata.set("containers", containers)
def remove_container_metadata(object_name):
"""Helper method to remove the data for a specific container"""
metadata = substance_painter.project.Metadata(OPENPYPE_METADATA_KEY)
containers = metadata.get(OPENPYPE_METADATA_CONTAINERS_KEY)
if containers:
containers.pop(object_name, None)
metadata.set("containers", containers)

View file

@ -5,20 +5,10 @@ from openpype.pipeline import CreatedInstance, AutoCreator
from openpype.pipeline import legacy_io
from openpype.client import get_asset_by_name
import substance_painter.project
def set_workfile_data(data, update=False):
if update:
data = get_workfile_data().update(data)
metadata = substance_painter.project.Metadata("OpenPype")
metadata.set("workfile", data)
def get_workfile_data():
metadata = substance_painter.project.Metadata("OpenPype")
return metadata.get("workfile") or {}
from openpype.hosts.substancepainter.api.pipeline import (
set_project_metadata,
get_project_metadata
)
class CreateWorkfile(AutoCreator):
"""Workfile auto-creator."""
@ -71,17 +61,20 @@ class CreateWorkfile(AutoCreator):
current_instance["task"] = task_name
current_instance["subset"] = subset_name
set_workfile_data(current_instance.data_to_store())
set_project_metadata("workfile", current_instance.data_to_store())
def collect_instances(self):
workfile = get_workfile_data()
workfile = get_project_metadata("workfile")
if not workfile:
return
self.create_instance_in_context_from_existing(workfile)
def update_instances(self, update_list):
for instance, _changes in update_list:
set_workfile_data(instance.data_to_store(), update=True)
# Update project's workfile metadata
data = get_project_metadata("workfile") or {}
data.update(instance.data_to_store())
set_project_metadata("workfile", data)
# Helper methods (this might get moved into Creator class)
def create_instance_in_context(self, subset_name, data):

View file

@ -2,27 +2,16 @@ from openpype.pipeline import (
load,
get_representation_path,
)
from openpype.hosts.substancepainter.api.pipeline import imprint_container
from openpype.hosts.substancepainter.api.pipeline import (
imprint_container,
set_container_metadata,
remove_container_metadata
)
import substance_painter.project
import qargparse
def set_container(key, container):
metadata = substance_painter.project.Metadata("OpenPype")
containers = metadata.get("containers") or {}
containers[key] = container
metadata.set("containers", containers)
def remove_container(key):
metadata = substance_painter.project.Metadata("OpenPype")
containers = metadata.get("containers")
if containers:
containers.pop(key, None)
metadata.set("containers", containers)
class SubstanceLoadProjectMesh(load.LoaderPlugin):
"""Load mesh for project"""
@ -48,10 +37,12 @@ class SubstanceLoadProjectMesh(load.LoaderPlugin):
)
]
container_key = "ProjectMesh"
def load(self, context, name, namespace, data):
# Get user inputs
import_cameras = data.get("import_cameras", True)
preserve_strokes = data.get("preserve_strokes", True)
if not substance_painter.project.is_open():
# Allow to 'initialize' a new project
# TODO: preferably these settings would come from the actual
@ -59,7 +50,7 @@ class SubstanceLoadProjectMesh(load.LoaderPlugin):
# visually similar to still allow artist decisions)
settings = substance_painter.project.Settings(
default_texture_resolution=4096,
import_cameras=data.get("import_cameras", True),
import_cameras=import_cameras,
)
substance_painter.project.create(
@ -70,8 +61,8 @@ class SubstanceLoadProjectMesh(load.LoaderPlugin):
else:
# Reload the mesh
settings = substance_painter.project.MeshReloadingSettings(
import_cameras=data.get("import_cameras", True),
preserve_strokes=data.get("preserve_strokes", True)
import_cameras=import_cameras,
preserve_strokes=preserve_strokes
)
def on_mesh_reload(status: substance_painter.project.ReloadMeshStatus): # noqa
@ -87,13 +78,21 @@ class SubstanceLoadProjectMesh(load.LoaderPlugin):
# Store container
container = {}
project_mesh_object_name = "_ProjectMesh_"
imprint_container(container,
name=self.container_key,
namespace=self.container_key,
name=project_mesh_object_name,
namespace=project_mesh_object_name,
context=context,
loader=self)
container["options"] = data
set_container(self.container_key, container)
# We want store some options for updating to keep consistent behavior
# from the user's original choice. We don't store 'preserve_strokes'
# as we always preserve strokes on updates.
container["options"] = {
"import_cameras": import_cameras,
}
set_container_metadata(project_mesh_object_name, container)
def switch(self, container, representation):
self.update(container, representation)
@ -107,7 +106,7 @@ class SubstanceLoadProjectMesh(load.LoaderPlugin):
container_options = container.get("options", {})
settings = substance_painter.project.MeshReloadingSettings(
import_cameras=container_options.get("import_cameras", True),
preserve_strokes=container_options.get("preserve_strokes", True)
preserve_strokes=True
)
def on_mesh_reload(status: substance_painter.project.ReloadMeshStatus):
@ -119,8 +118,9 @@ class SubstanceLoadProjectMesh(load.LoaderPlugin):
substance_painter.project.reload_mesh(path, settings, on_mesh_reload)
# Update container representation
container["representation"] = str(representation["_id"])
set_container(self.container_key, container)
object_name = container["objectName"]
update_data = {"representation": str(representation["_id"])}
set_container_metadata(object_name, update_data, update=True)
def remove(self, container):
@ -128,4 +128,4 @@ class SubstanceLoadProjectMesh(load.LoaderPlugin):
# or close the project?
# TODO: This is likely best 'hidden' away to the user because
# this will leave the project's mesh unmanaged.
remove_container(self.container_key)
remove_container_metadata(container["objectName"])