33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155 | class ImageCopernicusLoader(plugin.HoudiniLoader):
"""Load images into Copernicus network.
We prefer to create the node inside the 'active' Copernicus network
because Copernicus does not seem to have the equivalent of an
"Object Merge" COP node, so we cannot merge nodes from another Cop network.
"""
product_types = {
"imagesequence",
"review",
"render",
"plate",
"image",
"online",
}
label = "Load Image (Copernicus)"
representations = {"*"}
order = -10
icon = "code-fork"
color = "orange"
@classmethod
def apply_settings(cls, project_settings):
# Copernicus was introduced in Houdini 20.5.
if hou.applicationVersion() < (20, 5, 0):
cls.enabled = False
return None
return super().apply_settings(project_settings)
def load(self, context, name=None, namespace=None, data=None):
# Format file name, Houdini only wants forward slashes
path = self.filepath_from_context(context)
path = self.format_path(path, representation=context["representation"])
# Define node name
namespace = namespace if namespace else context["folder"]["name"]
node_name = "{}_{}".format(namespace, name) if namespace else name
# Create node in the active COP network
network = lib.find_active_network(
category=hou.copNodeTypeCategory(),
default=None
)
if network is None:
# If no active network, use a COP network
network = get_image_ayon_container()
node = network.createNode("file", node_name=node_name)
node.moveToGoodPosition()
node.setParms({
"filename": path,
# Add the default "C" file AOV
"aovs": 1,
"aov1": "C",
})
# Imprint it manually
data = {
"schema": "ayon:container-3.0",
"id": AYON_CONTAINER_ID,
"name": node_name,
"namespace": namespace,
"loader": str(self.__class__.__name__),
"representation": context["representation"]["id"],
}
lib.imprint(node, data, folder="AYON")
return node
def update(self, container, context):
repre_entity = context["representation"]
node = container["node"]
# Update the file path
file_path = self.filepath_from_context(context)
file_path = self.format_path(file_path, repre_entity)
parms = {
"filename": file_path,
"representation": repre_entity["id"],
}
# Update attributes
node.setParms(parms)
def remove(self, container):
node = container["node"]
# Let's clean up the IMAGES COP2 network
# if it ends up being empty and we deleted
# the last file node. Store the parent
# before we delete the node.
parent = node.parent()
node.destroy()
if parent.path() == f"{pipeline.AYON_CONTAINERS}/{COPNET_NAME}":
parent.destroy()
@staticmethod
def format_path(path, representation):
"""Format file path correctly for single image or sequence."""
ext = os.path.splitext(path)[-1]
# The path is either a single file or sequence in a folder.
is_sequence = bool(representation["context"].get("frame"))
if is_sequence:
folder, filename = os.path.split(path)
filename = re.sub(r"(.*)\.(\d+){}$".format(re.escape(ext)),
"\\1.$F4{}".format(ext),
filename)
path = os.path.join(folder, filename)
path = os.path.normpath(path)
path = path.replace("\\", "/")
return path
def switch(self, container, representation):
self.update(container, representation)
|