From 2e18e7867fe3f23d2f220ef0db45e9a6d3de70ce Mon Sep 17 00:00:00 2001 From: Marcus Ottosson Date: Sat, 24 Sep 2016 17:17:03 +0100 Subject: [PATCH] Refine interface and README.md --- README.md | 154 ++++++++++++++++--- pyblish_starter/__init__.py | 16 +- pyblish_starter/lib.py | 16 -- pyblish_starter/maya/__init__.py | 1 - pyblish_starter/maya/pipeline.py | 56 ++++--- pyblish_starter/pipeline.py | 37 ++++- pyblish_starter/plugins/extract_animation.py | 2 +- pyblish_starter/plugins/integrate_asset.py | 4 +- pyblish_starter/tools/builder/__init__.py | 0 9 files changed, 211 insertions(+), 75 deletions(-) create mode 100644 pyblish_starter/tools/builder/__init__.py diff --git a/README.md b/README.md index 175a37e..888211f 100644 --- a/README.md +++ b/README.md @@ -2,19 +2,51 @@ A basic asset creation pipeline - batteries included. -> WARNING: Not ready for use. +- **Keywords:** Film, games, content creation, pipeline + +- **Objective:** + 1. Introduce publishing to the techincal director + 1. Demonstrate where publishing fits within a typical production pipeline + 1. Inspire further expansion upon basic ideas + +- **Mission:** Demonstrate what is made possible with publishing. + +- **Motivation:** I'm doing this for the same reason I created Pyblish. Because I see publishing as *the single most important aspect of any production pipeline*. It is on top of the advantages that it provides that the surrounding pipeline is made possible - e.g. [browser](#looking-ahead) and [loader](#looking-ahead), [builder](#looking-ahead) and [manager](#looking-ahead). + +- **Requirements** Reliably output correct data with minimal impact on artist productivity. + +- **Technology** Starter is built upon [Pyblish](http://pyblish.com), [Python](https://www.python.org) and [bindings](https://github.com/mottosso/Qt.py) for [Qt](https://qt.io), and depends upon a Windows, Linux or MacOS operating system with [Autodesk Maya](http://www.autodesk.com/maya). + +- **Audience** Technical directors interested in pipeline working in small- to mid-sized companies with a thirst for better ways of working. + +
**Table of contents** - [Install](#install) - [Usage](#usage) - [Description](#description) +- [Batteries](#batteries) +- [Looking Ahead](#looking-ahead) - [Terminology](#terminology) +- [Shared/User Separation](#shareduser-separation) +- [Ids](#ids) +- [Starter API](#starter-api) +- [Host API](#host-api) - [Information Hierarchy](#information-hierarchy) - [Contract](#contract) - [`starter.model`](#startermodel) - [`starter.rig`](#starterrig) - [`starter.animation`](#starteranimation) +- [Example](#example) +- [Requirement Specification](#requirement-specification) + - [Workout](#workout) + - [Self-intersections](#self-intersections) + - [Extreme Acceleration](#extreme-acceleration) + - [Extreme Surface Tangency](#extreme-surface-tangency) + - [Extreme Surface Stretch or Compression](#extreme-surface-stretch-or-compression) +- Contributing +- Help

@@ -79,7 +111,48 @@ It includes a series of graphical user interfaces to aid the user in conforming In addition to Pyblish cooperative plug-ins, a series of template workflow utilities are included. -... + + +#### Creator + +A bla bla bla, and a bla bla. + +
+
+
+
+
+
+
+
+ + + +#### Loader + +A bla bla bla, and a bla bla. + +
+
+
+
+
+
+
+ +
+
+ +### Looking Ahead + +Without publishing, in any shape or form, the following essential tools are but a dream. + +| Name | Purpose | Description +|:------------------|:---------------------------------|:-------------- +| browser | remove file-system dependence | Search and clear presentation of available data relative a given project or task. +| loader | control what goes in | Keep tabs on where data comes from so as to enable tracking and builds. +| builder | associate disparate assets | Automatic composition of data that changes independently but need to somehow stay associated. +| manager | stay up to date | Notification and visualisation of data in time.

@@ -95,16 +168,16 @@ Starter reserves the following words for private and public use. Public members | ![][ver] | `version` | `X` | An asset iteration | v1, v034 | ![][rep] | `representation` | | A data format | Maya file, pointcache, thumbnail | ![][for] | `format` | | A file extension | `.ma`, `.abc`, `.ico`, `.png` -| ![][for] | `shared` | `X` | Public data | v034 of Ryan -| ![][for] | `user` | `X` | Private data | Scenefile for v034 of Ryan +| ![][shd] | `shared` | `X` | Public data | v034 of Ryan +| ![][usr] | `user` | `X` | Private data | Scenefile for v034 of Ryan
-### Shared/user separation +### Shared/User separation -This project separates between data in progress, and data shared with others. +Humans are imperfect, yet strive for perfection. In order to reduce the amount of friction produced through this contradiction, a separation is made between **user** and **shared** data. Artists are not required to work any differently, yet are able to produce correct data. -Data in progress is any data in which a shared data is being produced. It is highly **mutable** and typically **private** to an individual artist. +User data is highly **mutable** and typically **private** to an individual artist. - **Mutable** implies transient data that is likely to change at any given moment. - **Private** implies personal, highly irregular and likely invalid data. @@ -113,7 +186,7 @@ Shared data on the other hand is **immutable**, **correct** and **impersonal**. - **Immutable** implies that the data may be dependent upon by other data. - **Correct** implies passing validation of the associated family. -- **Impersonal** implies following strict organiasational conventions. +- **Impersonal** implies following strict organisational conventions. ### Ids @@ -121,8 +194,8 @@ Shared data on the other hand is **immutable**, **correct** and **impersonal**. | Name | Description | Example |:-----------------------------|:-------------------------|:---------- -| `pyblish.starter.container` | Incoming unit of data | `...:model_GRP`, `...:rig_GRP` -| `pyblish.starter.instance` | Outgoing unit of data | `Strange_model_default` +| `pyblish.starter.container` | Unit of incoming data | `...:model_GRP`, `...:rig_GRP` +| `pyblish.starter.instance` | Unit of outgoing data | `Strange_model_default`
@@ -131,6 +204,44 @@ Shared data on the other hand is **immutable**, **correct** and **impersonal**. [rep]: https://cloud.githubusercontent.com/assets/2152766/18759916/b2e3161c-80f6-11e6-9e0a-c959d63047a8.png [for]: https://cloud.githubusercontent.com/assets/2152766/18759918/b479168e-80f6-11e6-8d1c-aee4e654d335.png [pro]: https://cloud.githubusercontent.com/assets/2152766/18760901/d6bf24b4-80fa-11e6-8880-7a0e927c8c27.png +[usr]: https://cloud.githubusercontent.com/assets/2152766/18808940/eee150bc-8267-11e6-862f-a31e38d417af.png +[shd]: https://cloud.githubusercontent.com/assets/2152766/18808939/eeded22e-8267-11e6-9fcb-150208d55764.png + +
+
+ +### Starter API + +pyblish-starter provides a stateful API. State is set and modified by calling `pyblish_starter.install()`. +The following members are available via `pyblish_starter`. + +| Member | Returns | Description +|:--------------------------------|:---------|:-------- +| `install(host)` | `str` | Install Starter into the current interpreter session +| `uninstall()` | `str` | Revert installation +| `ls()` | `dict` | List available assets, relative `root` +| `root()` | `str` | Absolute path to current working directory +| `format_user_dir(root, name)` | `str` | Return absolute path or user directory relative arguments +| `format_shared_dir(root)` | `str` | Return absolute path of shared directory +| `format_version(version)` | `str` | Return file-system compatible string of `version` +| `find_latest_version(versions)` | `int` | Given a series of string-formatted versions, return the latest one +| `parse_version(version)` | `str` | Given an arbitrarily formatted string, return version number +| `register_root(root)` | | Register currently active root +| `register_host(host)` | | Register currently active host +| `register_plugins()` | | Register plug-ins bundled with Pyblish Starter +| `deregister_plugins()` | | +| `registered_host()` | `module` | Return currently registered host + +
+ +### Host API + +A host must implement the following members. + +| Member | Returns | Description +|:--------------------------|:--------|:-------- +| `create(name, family)` | `dict` | Build fixture for outgoing data (see [instance]()) +| `load(asset, version=-1)` | `str` | Import external data into [container]()

@@ -159,11 +270,11 @@ The mental and physical model for files and folders look like this. Starter defines these families. -| Family | Definition | Link -|:--------------------|:------------------------------------------------------------|:------------ -| `starter.model` | Geometry with deformable topology | [Spec](#startermodel) -| `starter.rig` | An articulated `starter.model` for animators | [Spec](#starterrig) -| `starter.animation` | Pointcached `starter.rig` for tech-anim and lighting | [Spec](#starteranimation) +| Family | Definition | Link +|:--------------------|:-----------------------------------------------|:------------ +| `starter.model` | Geometry with deformable topology | [Spec](#startermodel) +| `starter.rig` | An articulated `starter.model` for animators | [Spec](#starterrig) +| `starter.animation` | Pointcached `starter.rig` for rendering | [Spec](#starteranimation)
@@ -213,7 +324,7 @@ A generic representation of geometry. -The `starter.rig` contains the necessary implementation and interface for animators to produce +The `starter.rig` contains the necessary implementation and interface for animators to animate. ![aud][] **Target Audience** @@ -309,16 +420,11 @@ The following is an example of the minimal effort required to produce film with ##### Setup -Before any work can be done, you must initialise Starter. +Before any work can be done, you must install Starter. ```python -# Prerequisite -import pyblish_maya -pyblish_maya.setup() - -# Starter -import pyblish_starter -pyblish_starter.setup() +from pyblish_starter import install, maya +install(maya) ```
diff --git a/pyblish_starter/__init__.py b/pyblish_starter/__init__.py index 970bc3a..09c0b45 100644 --- a/pyblish_starter/__init__.py +++ b/pyblish_starter/__init__.py @@ -5,13 +5,17 @@ """ from .pipeline import ( - ls, install, uninstall, + ls, + register_root as root, # alias + register_root, register_host, register_plugins, + deregister_plugins, + registered_host, ) @@ -20,24 +24,28 @@ format_shared_dir, format_version, - find_next_version, + find_latest_version, parse_version, ) __all__ = [ - "ls", "install", "uninstall", + "ls", + "root", "register_host", "register_plugins", + "register_root", "registered_host", + "deregister_plugins", + "format_user_dir", "format_shared_dir", "format_version", - "find_next_version", + "find_latest_version", "parse_version", ] diff --git a/pyblish_starter/lib.py b/pyblish_starter/lib.py index 9937795..736d71c 100644 --- a/pyblish_starter/lib.py +++ b/pyblish_starter/lib.py @@ -129,19 +129,3 @@ def find_latest_version(versions): highest_version = version return highest_version - - -def find_next_version(versions): - """Return next version from list of versions - - See docstring for :func:`find_latest_version`. - - Arguments: - versions (list): Version numbers as string - - Returns: - int: Next version number - - """ - - return find_latest_version(versions) + 1 diff --git a/pyblish_starter/maya/__init__.py b/pyblish_starter/maya/__init__.py index 24a6284..d05f536 100644 --- a/pyblish_starter/maya/__init__.py +++ b/pyblish_starter/maya/__init__.py @@ -8,7 +8,6 @@ install, uninstall, - root, load, create, ) diff --git a/pyblish_starter/maya/pipeline.py b/pyblish_starter/maya/pipeline.py index cb8f59a..ac8eb05 100644 --- a/pyblish_starter/maya/pipeline.py +++ b/pyblish_starter/maya/pipeline.py @@ -22,19 +22,20 @@ def install(): except (ImportError, AssertionError): _display_missing_dependencies() - install_menu() - register_formats() + _install_menu() + _register_formats() + _register_root() def uninstall(): - uninstall_menu() - deregister_formats() + _uninstall_menu() + _deregister_formats() -def install_menu(): +def _install_menu(): from pyblish_starter.tools import creator, loader - uninstall_menu() + _uninstall_menu() def deferred(): cmds.menu(self.menu, @@ -48,7 +49,7 @@ def deferred(): QtCore.QTimer.singleShot(100, deferred) -def uninstall_menu(): +def _uninstall_menu(): widgets = dict((w.objectName(), w) for w in QtWidgets.qApp.allWidgets()) menu = widgets.get(self.menu) @@ -57,29 +58,31 @@ def uninstall_menu(): del(menu) -def register_formats(): +def _register_formats(): pipeline.register_format(".ma") pipeline.register_format(".mb") pipeline.register_format(".abc") -def deregister_formats(): +def _deregister_formats(): pipeline.deregister_format(".ma") pipeline.deregister_format(".mb") pipeline.deregister_format(".abc") -def root(): - """Return project-root or directory of current working file""" - return (cmds.workspace(rootDirectory=True, query=True) or - cmds.workspace(directory=True, query=True)) +def _register_root(): + """Register project root or directory of current working file""" + root = ( + cmds.workspace(rootDirectory=True, query=True) or + cmds.workspace(directory=True, query=True) + ) + + pipeline.register_root(root) def load(asset, version=-1): """Load asset - - Arguments: asset ("pyblish-starter:asset-1.0"): Asset which to import version (int, optional): Version number, defaults to latest @@ -152,6 +155,11 @@ def create(name, family): name (str): Name of instance family (str): Name of family + Raises: + NameError on `name` already exists + KeyError on invalid dynamic property + RuntimeError on host error + """ for item in pipeline.registered_families(): @@ -189,7 +197,7 @@ def create(name, family): cmds.select(instance, noExpand=True) # Display instance attributes to user - mel.eval("updateEditorToggleCheckboxes; copyAEWindow;") + # mel.eval("updateEditorToggleCheckboxes; copyAEWindow;") return instance @@ -210,13 +218,15 @@ def containerise(name, nodes, version): assemblies = cmds.ls(nodes, assemblies=True) container = cmds.group(assemblies, name=name) - for key, value in ( - ("id", "pyblish.starter.container"), - ("author", version["author"]), - ("loader", self.__name__), - ("time", version["time"]), - ("comment", version.get("comment", "")), - ): + data = [ + ("id", "pyblish.starter.container"), + ("author", version["author"]), + ("loader", self.__name__), + ("time", version["time"]), + ("comment", version.get("comment", "")) + ] + + for key, value in data: if not value: continue diff --git a/pyblish_starter/pipeline.py b/pyblish_starter/pipeline.py index 6f96ede..7b22320 100644 --- a/pyblish_starter/pipeline.py +++ b/pyblish_starter/pipeline.py @@ -15,6 +15,8 @@ self._registered_data = list() self._registered_families = list() self._registered_formats = list() +self._registered_root = os.getcwd() # Default to current working directory +self._is_installed = False def default_host(): @@ -78,6 +80,7 @@ def install(host): register_default_data() register_default_families() + self._is_installed = True self.log.info("Successfully installed Pyblish Starter!") @@ -95,6 +98,17 @@ def uninstall(): self.log.info("Successfully uninstalled Pyblish Starter!") +def is_installed(): + """Return state of installation + + Returns: + True if installed, False otherwise + + """ + + return self._is_installed + + def register_default_data(): register_data(key="id", value="pyblish.starter.instance") register_data(key="name", value="{name}") @@ -168,8 +182,7 @@ def ls(): """ - root = registered_host().root() - assetsdir = lib.format_shared_dir(root) + assetsdir = lib.format_shared_dir(_registered_root) for asset in lib.listdir(assetsdir): versionsdir = os.path.join(assetsdir, asset) @@ -204,14 +217,30 @@ def ls(): yield asset_entry +def registered_root(): + """Return currently registered root""" + return self._registered_root + + +def register_root(path): + """Register currently active root""" + self._registered_root = path + + def register_format(format): + """Register a supported format + + A supported format is used to determine which of any available + representations are relevant to the currently registered host. + + """ + self._registered_formats.append(format) def register_host(host): missing = list() - for member in ("root", - "load", + for member in ("load", "create",): if not hasattr(host, member): missing.append(member) diff --git a/pyblish_starter/plugins/extract_animation.py b/pyblish_starter/plugins/extract_animation.py index bd3e3b7..d7b96b4 100644 --- a/pyblish_starter/plugins/extract_animation.py +++ b/pyblish_starter/plugins/extract_animation.py @@ -36,7 +36,7 @@ def process(self, instance): except OSError: pass - filename = "{name}.ma".format(**instance.data) + filename = "{name}.abc".format(**instance.data) export_alembic( nodes=instance, diff --git a/pyblish_starter/plugins/integrate_asset.py b/pyblish_starter/plugins/integrate_asset.py index 1f79f0c..fc1b057 100644 --- a/pyblish_starter/plugins/integrate_asset.py +++ b/pyblish_starter/plugins/integrate_asset.py @@ -24,7 +24,7 @@ def process(self, instance): import shutil from pyblish_starter import ( format_version, - find_next_version, + find_latest_version, ) context = instance.context @@ -46,7 +46,7 @@ def process(self, instance): self.log.critical("An unexpected error occurred.") raise - version = find_next_version(os.listdir(instancedir)) + version = find_latest_version(os.listdir(instancedir)) + 1 versiondir = os.path.join( instancedir, format_version(version) diff --git a/pyblish_starter/tools/builder/__init__.py b/pyblish_starter/tools/builder/__init__.py new file mode 100644 index 0000000..e69de29