Skip to content

Commit

Permalink
Support C4D Load and Manage
Browse files Browse the repository at this point in the history
  • Loading branch information
jackyjacky1307 authored and rebeccaLinx committed Mar 1, 2023
1 parent 235cd2a commit c72539d
Show file tree
Hide file tree
Showing 2 changed files with 178 additions and 4 deletions.
62 changes: 59 additions & 3 deletions avalon/cinema4d/lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,60 @@
AVALON_TAB = "avalon"


def unique_namespace(namespace, format="%02d", prefix="", suffix=""):
"""Return unique namespace
Similar to :func:`unique_name` but evaluating namespaces
as opposed to object names.
Arguments:
namespace (str): Name of namespace to consider
format (str, optional): Formatting of the given iteration number
suffix (str, optional): Only consider namespaces with this suffix.
"""

iteration = 1
unique = prefix + (namespace + format % iteration) + suffix

# The `existing` set does not just contain the namespaces but *all* nodes
# within "current namespace". We need all because the namespace could
# also clash with a node name. To be truly unique and valid one needs to
# check against all.
doc = c4d.documents.GetActiveDocument()
iterator = doc.GetFirstObject()
existing = [node[c4d.ID_CA_XREF_NAMESPACE]
for node in iterate_nodes(iterator, node_type="XRef")]
while unique in existing:
iteration += 1
unique = prefix + (namespace + format % iteration) + suffix

return unique


def iterate_nodes(iterator, node_type=None):
while iterator:
if node_type and iterator.GetTypeName() != node_type:
pass
else:
yield iterator
for node in iterate_nodes(iterator.GetDown(), node_type=node_type):
yield node
iterator = iterator.GetNext()


def iterate_children(node, node_type=None):
iterator = node.GetDown()
while iterator:
if node_type and iterator.GetTypeName() != node_type:
pass
else:
yield iterator
for node in iterate_children(iterator, node_type=node_type):
yield node
iterator = iterator.GetNext()


def read(obj):
"""Return user-defined attributes from `obj`"""

Expand Down Expand Up @@ -38,12 +92,14 @@ def imprint(obj, data):
data (dict): Dictionary of key/value pairs
"""

exists_keys = []
exists_keys = {}
for cid, bc in obj.GetUserDataContainer():
exists_keys.append(bc[c4d.DESC_NAME])
exists_keys[bc[c4d.DESC_NAME]] = cid

for key, value in data.items():
if key in exists_keys:
if key in exists_keys.keys():
cid = exists_keys[key]
obj[cid] = value
continue

if callable(value):
Expand Down
120 changes: 119 additions & 1 deletion avalon/cinema4d/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@
from ..lib import logger, find_submodule
from .. import api

from ..pipeline import AVALON_CONTAINER_ID

self = sys.modules[__name__]
self._menu = None

AVALON_CONTAINERS = "AVALON_CONTAINERS"


class AvalonContextLabel(c4d.plugins.CommandData):
PLUGIN_ID = 999000001
Expand Down Expand Up @@ -202,10 +206,123 @@ def _uninstall_menu():
"""


def containerise(name,
namespace,
nodes,
context,
loader=None,
suffix="CON"):
"""Bundle `nodes` into an assembly and imprint it with metadata
Containerisation enables a tracking of version, author and origin
for loaded assets.
Arguments:
name (str): Name of resulting assembly
namespace (str): Namespace under which to host container
nodes (list): Long names of nodes to containerise
context (dict): Asset information
loader (str, optional): Name of loader used to produce this container.
suffix (str, optional): Suffix of container, defaults to `_CON`.
Returns:
container (str): Name of container assembly
"""
doc = c4d.documents.GetActiveDocument()

container = c4d.BaseObject(c4d.Oselection)
doc.InsertObject(container)
container[c4d.ID_BASELIST_NAME] = "%s_%s_%s" % (namespace, name, suffix)

data = {
"schema": "avalon-core:container-2.0",
"id": AVALON_CONTAINER_ID,
"name": name,
"namespace": namespace,
"loader": str(loader),
"representation": str(context["representation"]["_id"])
}

lib.imprint(container, data)

main_container = doc.SearchObject(AVALON_CONTAINERS)
if not main_container:
main_container = c4d.BaseObject(c4d.Oselection)
doc.InsertObject(main_container)
main_container[c4d.ID_BASELIST_NAME] = AVALON_CONTAINERS

# Hide main_container in Object Manager
main_container.ChangeNBit(c4d.NBIT_OHIDE, c4d.NBITCONTROL_TOGGLE)

selection_list = main_container[c4d.SELECTIONOBJECT_LIST]
selection_list.InsertObject(container, 1)
main_container[c4d.SELECTIONOBJECT_LIST] = selection_list

# Hide container in Object Manager
container.ChangeNBit(c4d.NBIT_OHIDE, c4d.NBITCONTROL_TOGGLE)

return container


def parse_container(container):
"""Return the container node's full container data.
Args:
container (node): A container node.
Returns:
dict: The container schema data for this container node.
"""
data = lib.read(container)

# Backwards compatibility pre-schemas for containers
data["schema"] = data.get("schema", "avalon-core:container-1.0")

# Append transient data
data["objectName"] = container[c4d.ID_BASELIST_NAME]

return data


def _ls():
"""Yields Avalon container node names.
Used by `ls()` to retrieve the nodes and then query the full container's
data.
Yields:
str: Avalon container node name (Selection Object)
"""

ids = {AVALON_CONTAINER_ID,
# Backwards compatibility
"pyblish.mindbender.container"}

# Iterate over all 'Selection Object' nodes in the scene to detect
# whether they have the avalon container ".id" attribute.
doc = c4d.documents.GetActiveDocument()
iterator = doc.GetFirstObject()
for node in lib.iterate_nodes(iterator, node_type="Selection"):

for cid, bc in node.GetUserDataContainer():
if bc[c4d.DESC_NAME] == "id":
value = node[cid]
if value in ids:
yield node
break


def ls():
"""Yields containers from active Cinema 4D scene
"""
return []
containers = _ls()

for container in containers:
data = parse_container(container)
yield data


class Creator(api.Creator):
Expand All @@ -215,6 +332,7 @@ def process(self):
doc = c4d.documents.GetActiveDocument()
instance = c4d.BaseObject(c4d.Oselection)
instance[c4d.ID_BASELIST_NAME] = self.name
instance.ChangeNBit(c4d.NBIT_NO_DELETE, c4d.NBITCONTROL_TOGGLE)
lib.imprint(instance, self.data)

if (self.options or {}).get("useSelection"):
Expand Down

0 comments on commit c72539d

Please sign in to comment.