Files
blender-portable-repo/scripts/addons/asset_pipeline/merge/naming.py
T
2026-03-17 14:58:51 -06:00

239 lines
8.0 KiB
Python

# SPDX-FileCopyrightText: 2021 Blender Studio Tools Authors
#
# SPDX-License-Identifier: GPL-3.0-or-later
import bpy
from bpy_extras.id_map_utils import get_id_reference_map, get_all_referenced_ids
from .util import get_storage_of_id
from .. import constants, config
from .util import data_type_from_transfer_data_key
def merge_get_target_suffix(suffix: str) -> str:
"""Get the corresponding suffix for a given suffix
Args:
suffix (str): Suffix for External or Local Datablock
Returns:
str: Returns External Suffix if given Local suffix for vice-versa
"""
if suffix.endswith(constants.EXTERNAL_SUFFIX):
return constants.LOCAL_SUFFIX
if suffix.endswith(constants.LOCAL_SUFFIX):
return constants.EXTERNAL_SUFFIX
def merge_get_target_name(name: str) -> str:
"""Get the corresponding target name for a given datablock's suffix.
Suffixes are set by the add_suffix_to_hierarchy() function prior to
calling this function.
Args:
name (str): Name of a given datablock including its suffix
Returns:
str: Returns datablock name with the opposite suffix
"""
old = name.split(constants.MERGE_DELIMITER)[-1]
new = merge_get_target_suffix(old)
assert new, "Failed to flip the LOC/EXT suffix of this name, this should never happen : " + name
li = name.rsplit(old, 1)
return new.join(li)
def merge_get_basename(name: str) -> str:
"""Returns the name of an asset without its suffix"""
if name.endswith(constants.LOCAL_SUFFIX) or name.endswith(
constants.EXTERNAL_SUFFIX
):
return constants.MERGE_DELIMITER.join(
name.split(constants.MERGE_DELIMITER)[:-1]
)
return name
def merge_remove_suffix_from_hierarchy(collection: bpy.types.Collection) -> None:
"""Removes the suffix after a set delimiter from all datablocks
referenced by a collection, itself included
Args:
collection (bpy.types.Collection): Collection that as been suffixed
"""
ref_map = get_id_reference_map()
datablocks = get_all_referenced_ids(collection, ref_map)
datablocks.add(collection)
for action in bpy.data.actions:
datablocks.add(action)
for db in datablocks:
if db == None:
# Not sure why this would happen.
raise Exception(
f"None value in datablock list"
)
if db.library:
# Don't rename linked datablocks.
continue
try:
db.name = merge_get_basename(db.name)
except:
pass
def merge_add_suffix_to_hierarchy(
collection: bpy.types.Collection, suffix_base: str
) -> None:
"""Add a suffix to the names of all datablocks referenced by a collection,
itself included.
Args:
collection (bpy.types.Collection): Collection that needs to be suffixed
suffix_base (str): Suffix to append to collection and items linked to collection
"""
suffix = f"{constants.MERGE_DELIMITER}{suffix_base}"
ref_map = get_id_reference_map()
datablocks = get_all_referenced_ids(collection, ref_map)
datablocks.add(collection)
for db in datablocks:
if db == None:
# Not sure why this would happen.
continue
if len(db.name) > 59:
raise Exception(
f"Datablock name too long, must be max 59 characters: {db.name}"
)
if db.library:
# Don't rename linked datablocks.
continue
collision_db = get_storage_of_id(db).get(db.name + suffix)
if collision_db:
collision_db.name += f'{constants.MERGE_DELIMITER}OLD'
try:
new_name = db.name + suffix
db.name = new_name
assert (
db.name == new_name
), "This should never happen here, unless some add-on suffix is >3 characters. Avoid!"
except:
pass
def asset_prefix_name_get(name: str) -> str:
"""Returns a string with the asset prefix if it is not already set.
Users can specify a prefix to live on all objects during the
asset creation process. This prefix is stored in the scene.
Args:
name (str): Name to add prefix to
Returns:
str: Returns name with prefix
"""
asset_pipe = bpy.context.scene.asset_pipeline
if name.startswith(asset_pipe.prefix + constants.NAME_DELIMITER):
return name
prefix = (
asset_pipe.prefix + constants.NAME_DELIMITER if asset_pipe.prefix != "" else ""
)
return prefix + name
def task_layer_prefix_name_get(name: str, task_layer_owner: str) -> str:
"""Returns a string with the task layer prefix if one is not already set.
Prefix for assets is defined task_layer.json file within TASK_LAYER_TYPES
Will return early if any prefix is found, cannot replace existing prefixes.
Args:
name (str): Name to add prefix to
task_layer_owner (str):
Returns:
str: Returns name with prefix
"""
prefix = config.TASK_LAYER_TYPES[task_layer_owner]
return prefix + constants.NAME_DELIMITER + task_layer_prefix_basename_get(name)
def task_layer_prefix_basename_get(name: str) -> str:
"""Get the base of a name if it contains a task layer prefix.
This prefix is set on some Transferable Data items, this functions
removes the prefixes and returns the basename
Args:
name (str): Original name including prefix
Returns:
str: Returns name without task layer prefix
"""
for task_layer_key in config.TASK_LAYER_TYPES:
prefix = config.TASK_LAYER_TYPES[task_layer_key] + constants.NAME_DELIMITER
if name.startswith(prefix):
return name[len(prefix):]
return name
def task_layer_prefix_legacy_basename(name) -> str:
# TODO Remove this is legacy code (coordinate with team)
if "." in name:
legacy_name = name.replace(".", "")
for task_layer_key in config.TASK_LAYER_TYPES:
if legacy_name.startswith(config.TASK_LAYER_TYPES[task_layer_key]):
return legacy_name.replace(config.TASK_LAYER_TYPES[task_layer_key], "")
def task_layer_prefix_transfer_data_update(
transfer_data_item: bpy.types.CollectionProperty,
) -> bool:
"""Task Layer Prefix can become out of date with the actual owner of the task layer.
This will update the existing prefixes on transfer_data_item so it can match the
owner of that transfer_data_item. Will update both the transfer_data_item.name and the
name of the actual data the transfer_data_item is referring to.
Args:
transfer_data_item (bpy.types.CollectionProperty): Transferable Data Item that is named with prefix
Returns:
bool: Returns True if a change to the name was completed
"""
prefix_types = [constants.MODIFIER_KEY, constants.CONSTRAINT_KEY]
if transfer_data_item.type not in prefix_types:
return
obj = transfer_data_item.id_data
td_data = data_type_from_transfer_data_key(obj, transfer_data_item.type)
# TODO Remove this
# Legacy Prefix Name was used during add-on testing stage but not production
legacy_name = task_layer_prefix_legacy_basename(transfer_data_item.name)
if legacy_name:
base_name = legacy_name
else:
base_name = task_layer_prefix_basename_get(transfer_data_item.name)
prefix = config.TASK_LAYER_TYPES[transfer_data_item.owner]
new_name = prefix + constants.NAME_DELIMITER + base_name
if new_name == transfer_data_item.name or not td_data.get(transfer_data_item.name):
return
# Ensure no period in name
# TODO Remove this is legacy code (coordinate with team)
new_name = new_name.replace(".", "")
td_data[transfer_data_item.name].name = new_name
transfer_data_item.name = new_name
return True
def get_id_type_name(id_type: bpy.types) -> str:
"""Return the cosmetic name of a given ID type
Args:
id_type (bpy.types): An ID type e.g. bpy.types.Object
Returns:
str: Name of an ID type e.g. bpy.types.Object will return 'Object'
"""
return str(id_type).split("'bpy_types.")[1].replace("'>", "")