Skip to content

project_creation

add_graphs_to_package(parsed_graph_names, parsed_dependencies, temp_package_filepath)

Add graphs to the temp package

Parameters:

Name Type Description Default
parsed_graph_names list

parsed graph names

required
parsed_dependencies list

parsed dependencies

required
temp_package_filepath str

temp package filepath

required
Source code in client/ayon_substancedesigner/api/project_creation.py
 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
def add_graphs_to_package(
        parsed_graph_names, parsed_dependencies, temp_package_filepath):
    """Add graphs to the temp package

    Args:
        parsed_graph_names (list): parsed graph names
        parsed_dependencies (list): parsed dependencies
        temp_package_filepath (str): temp package filepath

    """
    # Parse the temp package file
    unsaved_tree = etree.parse(temp_package_filepath)
    unsaved_root = unsaved_tree.getroot()

    # Find the <content> element in Unsaved_Package.xml
    content_element = unsaved_root.find('content')

    # Remove the existing <content/> element if it exists
    if content_element is not None:
        unsaved_root.remove(content_element)
    # Create a new <content> element and append the copied <graph> element
    new_content = etree.Element('content')
    new_content.extend(parsed_graph_names)  # Append the copied <graph> element
    unsaved_root.append(new_content)   # Add the new <content> to the root

    if parsed_dependencies:
        # Remove the existing <dependencies/> element if it exists
        dependencies_element = unsaved_root.find('dependencies')
        if dependencies_element is not None:
            # Find the <dependencies> element in Unsaved_Package.xml
            unsaved_root.remove(dependencies_element)

        new_dependencies_content = etree.Element('dependencies')
        # Append the copied <dependency> element
        new_dependencies_content.extend(parsed_dependencies)
        # Add the new <dependencies> to the root
        unsaved_root.append(new_dependencies_content)

    # Save the modified content for Substance file
    unsaved_tree.write(
        temp_package_filepath,
        encoding='utf-8',
        xml_declaration=True
    )

    log.info("All graphs are copied and pasted successfully!")

create_project_with_from_template(project_settings=None)

Create Project from template setting

Parameters:

Name Type Description Default
project_settings str

project settings. Defaults to None.

None
Source code in client/ayon_substancedesigner/api/project_creation.py
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
def create_project_with_from_template(project_settings=None):
    """Create Project from template setting

    Args:
        project_settings (str, optional): project settings. Defaults to None.
    """
    sd_context = sd.getContext()
    sd_app = sd_context.getSDApplication()
    sd_pkg_mgr = sd_app.getPackageMgr()

    if project_settings is None:
        project_settings = get_current_project_settings()

    context = get_current_context()
    project_name = context["project_name"]

    resources_dir = sd_app.getPath(SDApplicationPath.DefaultResourcesDir)
    project_creation_settings = project_settings["substancedesigner"].get(
        "project_creation", {})

    project_template_settings = project_creation_settings.get(
        "project_templates", [])
    if not project_creation_settings:
        return

    parsed_graph_names = []
    output_res_by_graphs = {}
    parsed_dependencies = []
    for project_template_setting in project_template_settings:
        graph_name = project_template_setting["grpah_name"]
        if project_template_setting["template_type"] == (
            "default_substance_template"
            ):
                project_template = project_template_setting.get(
                    "default_substance_template")
                template_filepath = get_template_filename_from_project(
                    resources_dir, project_template
                )
        elif project_template_setting["template_type"] == (
            "custom_template"
            ):
                custom_template = project_template_setting["custom_template"]
                project_template = custom_template["custom_template_graph"]
                if not project_template:
                    log.warning("Project template not set. "
                                "Skipping project creation.")
                    continue

                path = custom_template["custom_template_path"]
                if not path:
                    log.warning("Template path not filled. "
                                "Skipping project creation.")
                    continue
                folder_entity, task_entity = _get_current_context_entities(
                    context)
                template_filepath = resolve_template_path(
                    path, project_name, folder_entity, task_entity
                )
                if not os.path.exists(template_filepath):
                    log.warning(
                        f"Template path '{template_filepath}' "
                        "does not exist yet.")
                    continue
        else:
            task_type_template = project_template_setting["task_type_template"]
            folder_entity, task_entity = _get_current_context_entities(context)
            filter_data = {
                "task_types": task_type_template["task_types"]
            }
            matched_task_type = filter_profiles(
                project_template_settings, filter_data, logger=log)
            if not matched_task_type:
                log.warning("No matching task_type found. "
                            "Skipping project creation.")
                continue

            path = task_type_template["path"]
            template_filepath = resolve_template_path(
                path, project_name, folder_entity, task_entity)
            if not os.path.exists(template_filepath):
                log.warning(f"Template filepath '{template_filepath}'"
                            " not found.")
                continue

            project_template = task_entity["name"]

        template_filepath = os.path.normpath(template_filepath)

        parsed_graph = parse_graph_from_template(
            graph_name, project_template, template_filepath)
        if parsed_graph is not None:
            parsed_graph_names.append(parsed_graph)

        output_res_by_graphs[graph_name] = (
            project_template_setting["default_texture_resolution"]
        )
        parsed_dependency_paths = parse_dependencies_from_template(
            template_filepath)
        parsed_dependencies.extend(parsed_dependency_paths)

    if not parsed_graph_names:
        return

    # add graph with template
    package, package_filepath = create_tmp_package_for_template(
        sd_pkg_mgr, project_name
    )

    add_graphs_to_package(
        parsed_graph_names, parsed_dependencies, package_filepath
    )

    sd_pkg_mgr.unloadUserPackage(package)
    sd_pkg_mgr.loadUserPackage(
        package_filepath, updatePackages=True, reloadIfModified=True
    )

    # set user-defined resolution by graphs
    set_output_resolution_by_graphs(output_res_by_graphs)

create_tmp_package_for_template(sd_pkg_mgr, project_name)

Create temp substance package for template graph

Parameters:

Name Type Description Default
sd_pkg_mgr SDPackageMgr

package manager

required
project_name str

project_name

required

Returns:

Type Description

sd.api.sdpackage.SDPackage, str: SD Package and template file path

Source code in client/ayon_substancedesigner/api/project_creation.py
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
def create_tmp_package_for_template(sd_pkg_mgr, project_name):
    """Create temp substance package for template graph

    Args:
        sd_pkg_mgr (sd.api.sdpackagemgr.SDPackageMgr): package manager
        project_name (str): project_name

    Returns:
        sd.api.sdpackage.SDPackage, str: SD Package and template file path

    """
    temp_filename = "temp_ayon_package.sbs"
    for temp_package in sd_pkg_mgr.getUserPackages():
        path = temp_package.getFilePath()
        if os.path.basename(path) == temp_filename:
            return temp_package, path

    temp_package = sd_pkg_mgr.newUserPackage()
    staging_dir = tempdir.get_temp_dir(
        project_name, use_local_temp=True
    )
    path = os.path.join(staging_dir, temp_filename)
    path = os.path.normpath(path)
    sd_pkg_mgr.savePackageAs(temp_package, fileAbsPath=path)

    return temp_package, path

get_template_filename_from_project(resources_dir, project_template)

Get template filename from ayon project settings

Parameters:

Name Type Description Default
resources_dir SDApplicationPath

resources dir

required
project_template str

project template name

required

Returns:

Name Type Description
str

absolute filepath of the sbs template file.

Source code in client/ayon_substancedesigner/api/project_creation.py
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
def get_template_filename_from_project(resources_dir,
                                       project_template):
    """Get template filename from ayon project settings

    Args:
        resources_dir (sd.api.sdapplication.SDApplicationPath): resources dir
        project_template (str): project template name

    Returns:
        str: absolute filepath of the sbs template file.
    """
    templates_dir = os.path.join(resources_dir, "templates")
    if project_template == "empty":
        return os.path.join(templates_dir, "01_empty.sbs")
    if project_template in [
        "metallic_roughness",
        "metallic_roughness_anisotropy",
        "metallic_roughness_coated",
        "metallic_roughness_sheen",
        "adobe_standard_material"
    ]:
        return os.path.join(
            templates_dir, "02_pbr_metallic_roughness.sbs")
    elif project_template == "specular_glossiness":
        return os.path.join(
            templates_dir, "03_pbr_specular_glossiness.sbs")
    elif project_template == "blinn":
        return os.path.join(
            templates_dir, "04_blinn.sbs")
    elif project_template == "scan_metallic_roughness":
        return os.path.join(
            templates_dir, "05_scan_pbr_metallic_roughness.sbs")
    elif project_template == "scan_specular_glossiness":
        return os.path.join(
            templates_dir, "06_scan_pbr_specular_glossiness.sbs")
    elif project_template == "axf_to_metallic_roughness":
        return os.path.join(
            templates_dir, "07_axf_to_pbr_metallic_roughness.sbs")
    elif project_template == "axf_to_specular_glossiness":
        return os.path.join(
            templates_dir, "08_axf_to_pbr_specular_glossiness.sbs")
    elif project_template == "axf_to_axf":
        return os.path.join(
            templates_dir, "09_axf_to_axf.sbs")
    elif project_template == "studio_panorama":
        return os.path.join(
            templates_dir, "10_studio_panorama.sbs")
    elif project_template in [
        "sp_filter_generic",
        "sp_filter_specific",
        "sp_filter_channel_mesh_maps",
        "sp_generator_mesh_maps"
    ]:
        return os.path.join(
            templates_dir, "11_substance_painter.sbs")
    elif project_template == "sample_filter":
        return os.path.join(
            templates_dir, "12_substance_sampler.sbs")
    elif project_template == "clo_metallic_roughness":
        return os.path.join(
            templates_dir, "13_clo_metallic_roughness.sbs")

    return None

parse_dependencies_from_template(template_filepath)

Parse dependencies from Substance template file

Parameters:

Name Type Description Default
template_filepath str

Substance template filepath

required

Returns:

Type Description

List[xml.etree.ElementTree.Element]: dependencies from the select template

Source code in client/ayon_substancedesigner/api/project_creation.py
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
def parse_dependencies_from_template(template_filepath):
    """Parse dependencies from Substance template file

    Args:
        template_filepath (str): Substance template filepath

    Returns:
        List[xml.etree.ElementTree.Element]: dependencies from
            the select template
    """
    dependencies = []
    # Parse the template substance file
    substance_tree = etree.parse(template_filepath)
    substance_root = substance_tree.getroot()

    for element in substance_root.find('.//dependencies'):
        dependencies.append(element)
    return dependencies

parse_graph_from_template(graph_name, project_template, template_filepath)

Parse graph by project template name from Substance template file Args: graph_name (str): graph_name project_template (str): project template name template_filepath (str): Substance template filepath

Returns:

Type Description

List[xml.etree.ElementTree.Element]: graph(s) from the select template

Source code in client/ayon_substancedesigner/api/project_creation.py
30
31
32
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
def parse_graph_from_template(graph_name, project_template, template_filepath):
    """Parse graph by project template name from Substance template file
    Args:
        graph_name (str): graph_name
        project_template (str): project template name
        template_filepath (str): Substance template filepath

    Returns:
        List[xml.etree.ElementTree.Element]: graph(s) from the select template

    """
    # Parse the template substance file
    substance_tree = etree.parse(template_filepath)
    substance_root = substance_tree.getroot()

    # Find the <graph> element with the specified identifier
    graph_element = None
    for graph in substance_root.findall('.//graph'):
        identifier = graph.find('identifier')
        if identifier is not None and (
            identifier.attrib.get('v') == project_template
            ):
                graph_element = graph
                break

    if graph_element:
        identifier_element = graph_element.find('identifier')
        if identifier.attrib.get('v') == project_template:
            identifier_element.attrib['v'] = graph_name
    else:
        log.warning(
            f"Graph with identifier '{project_template}' "
            f"not found in {template_filepath}."
        )

    return graph_element

resolve_template_path(path, project_name, folder_entity, task_entity)

resolve template path for Substance files

Parameters:

Name Type Description Default
path _type_

template path to resolve

required
project_name str

project name

required
folder_entity dict

folder entity data

required
task_name str

task name

required

Returns:

Name Type Description
str

resolved path for Substance template file

Source code in client/ayon_substancedesigner/api/project_creation.py
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
def resolve_template_path(path, project_name, folder_entity, task_entity):
    """resolve template path for Substance files

    Args:
        path (_type_): template path to resolve
        project_name (str): project name
        folder_entity (dict): folder entity data
        task_name (str): task name

    Returns:
        str: resolved path for Substance template file
    """
    anatomy = Anatomy(project_name)
    project_entity = ayon_api.get_project(project_name)
    fill_data = get_template_data(
        project_entity, folder_entity, task_entity)
    fill_data["root"] = anatomy.roots
    result = StringTemplate.format_template(path, fill_data)
    if result.solved:
        path = result.normalized()
    return path

set_output_resolution_by_graphs(resolution_size_by_graphs)

Set output resolution per graph accordingly to Ayon settings

Parameters:

Name Type Description Default
package SDPackage

temp package for graphs

required
resolution_size_by_graphs dict

resolution data for each graph

required
Source code in client/ayon_substancedesigner/api/project_creation.py
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
def set_output_resolution_by_graphs(resolution_size_by_graphs):
    """Set output resolution per graph accordingly to Ayon settings

    Args:
        package (sd.api.sdpackage.SDPackage): temp package for graphs
        resolution_size_by_graphs (dict): resolution data for each graph
    """
    for graph_name, res_size in resolution_size_by_graphs.items():
        graph = get_sd_graph_by_name(graph_name)
        output_size = graph.getPropertyFromId(
                "$outputsize", SDPropertyCategory.Input
        )
        graph.setPropertyInheritanceMethod(
            output_size, SDPropertyInheritanceMethod.Absolute
        )
        graph.setPropertyValue(
            output_size, SDValueInt2.sNew(int2(res_size, res_size))
        )