# SPDX-License-Identifier: MIT OR GPL-3.0-or-later import re import warnings from collections.abc import Mapping, Sequence from collections.abc import Set as AbstractSet from typing import TYPE_CHECKING, ClassVar, Optional, Protocol import bpy from bpy.app.translations import pgettext from bpy.props import IntProperty, StringProperty from bpy.types import ( ID, Armature, Context, Event, FCurve, Material, Node, NodeSocket, NodeTree, Object, Operator, ShaderNodeGroup, ShaderNodeMapping, ShaderNodeTexImage, ShaderNodeTree, ShaderNodeValue, ShaderNodeVectorMath, UILayout, ) from ...common import ops, shader from ...common.human_bone_mapper.human_bone_mapper import create_human_bone_mapping from ...common.logger import get_logger from ...common.vrm0.human_bone import HumanBoneName as Vrm0HumanBoneName from ...common.vrm1.human_bone import ( HumanBoneName, HumanBoneSpecification, HumanBoneSpecifications, ) from ..extension import get_armature_extension from ..ops import VRM_OT_open_url_in_web_browser, layout_operator from ..property_group import BonePropertyGroup from ..vrm0.property_group import Vrm0HumanoidPropertyGroup from .property_group import ( Vrm1ExpressionPropertyGroup, Vrm1HumanBonesPropertyGroup, ) logger = get_logger(__name__) class VRM_OT_add_vrm1_meta_author(Operator): bl_idname = "vrm.add_vrm1_meta_author" bl_label = "Add Author" bl_description = "Add VRM 1.0 Meta Author" bl_options: AbstractSet[str] = {"REGISTER", "UNDO"} armature_object_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def update_armature_name(self, _context: Context) -> None: message = ( f"`{type(self).__qualname__}.armature_name` is deprecated" + " and will be removed in the next major release." + f" `Please use {type(self).__qualname__}.armature_object_name` instead." ) logger.warning(message) warnings.warn(message, DeprecationWarning, stacklevel=5) armature_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, update=update_armature_name, ) """`armature_name` is deprecated and will be removed in the next major release. Please use `armature_object_name` instead. """ def execute(self, context: Context) -> set[str]: if not self.armature_object_name and self.armature_name: self.armature_object_name = self.armature_name armature = context.blend_data.objects.get(self.armature_object_name) if armature is None or armature.type != "ARMATURE": return {"CANCELLED"} armature_data = armature.data if not isinstance(armature_data, Armature): return {"CANCELLED"} meta = get_armature_extension(armature_data).vrm1.meta author = meta.authors.add() author.value = "" meta.active_author_index = len(meta.authors) - 1 return {"FINISHED"} if TYPE_CHECKING: # This code is auto generated. # To regenerate, run the `uv run tools/property_typing.py` command. armature_object_name: str # type: ignore[no-redef] armature_name: str # type: ignore[no-redef] class VRM_OT_remove_vrm1_meta_author(Operator): bl_idname = "vrm.remove_vrm1_meta_author" bl_label = "Remove Author" bl_description = "Remove VRM 1.0 Meta Author" bl_options: AbstractSet[str] = {"REGISTER", "UNDO"} armature_object_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def update_armature_name(self, _context: Context) -> None: message = ( f"`{type(self).__qualname__}.armature_name` is deprecated" + " and will be removed in the next major release." + f" `Please use {type(self).__qualname__}.armature_object_name` instead." ) logger.warning(message) warnings.warn(message, DeprecationWarning, stacklevel=5) armature_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, update=update_armature_name, ) """`armature_name` is deprecated and will be removed in the next major release. Please use `armature_object_name` instead. """ author_index: IntProperty( # type: ignore[valid-type] options={"HIDDEN"}, min=0, ) def execute(self, context: Context) -> set[str]: if not self.armature_object_name and self.armature_name: self.armature_object_name = self.armature_name armature = context.blend_data.objects.get(self.armature_object_name) if armature is None or armature.type != "ARMATURE": return {"CANCELLED"} armature_data = armature.data if not isinstance(armature_data, Armature): return {"CANCELLED"} meta = get_armature_extension(armature_data).vrm1.meta if len(meta.authors) <= self.author_index: return {"CANCELLED"} meta.authors.remove(self.author_index) meta.active_author_index = min( meta.active_author_index, max(0, len(meta.authors) - 1), ) return {"FINISHED"} if TYPE_CHECKING: # This code is auto generated. # To regenerate, run the `uv run tools/property_typing.py` command. armature_object_name: str # type: ignore[no-redef] armature_name: str # type: ignore[no-redef] author_index: int # type: ignore[no-redef] class VRM_OT_move_up_vrm1_meta_author(Operator): bl_idname = "vrm.move_up_vrm1_meta_author" bl_label = "Move Up Author" bl_description = "Move Up VRM 1.0 Meta Author" bl_options: AbstractSet[str] = {"REGISTER", "UNDO"} armature_object_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def update_armature_name(self, _context: Context) -> None: message = ( f"`{type(self).__qualname__}.armature_name` is deprecated" + " and will be removed in the next major release." + f" `Please use {type(self).__qualname__}.armature_object_name` instead." ) logger.warning(message) warnings.warn(message, DeprecationWarning, stacklevel=5) armature_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, update=update_armature_name, ) """`armature_name` is deprecated and will be removed in the next major release. Please use `armature_object_name` instead. """ author_index: IntProperty( # type: ignore[valid-type] options={"HIDDEN"}, min=0, ) def execute(self, context: Context) -> set[str]: if not self.armature_object_name and self.armature_name: self.armature_object_name = self.armature_name armature = context.blend_data.objects.get(self.armature_object_name) if armature is None or armature.type != "ARMATURE": return {"CANCELLED"} armature_data = armature.data if not isinstance(armature_data, Armature): return {"CANCELLED"} meta = get_armature_extension(armature_data).vrm1.meta if len(meta.authors) <= self.author_index: return {"CANCELLED"} new_index = (self.author_index - 1) % len(meta.authors) meta.authors.move(self.author_index, new_index) meta.active_author_index = new_index return {"FINISHED"} if TYPE_CHECKING: # This code is auto generated. # To regenerate, run the `uv run tools/property_typing.py` command. armature_object_name: str # type: ignore[no-redef] armature_name: str # type: ignore[no-redef] author_index: int # type: ignore[no-redef] class VRM_OT_move_down_vrm1_meta_author(Operator): bl_idname = "vrm.move_down_vrm1_meta_author" bl_label = "Move Down Author" bl_description = "Move Down VRM 1.0 Meta Author" bl_options: AbstractSet[str] = {"REGISTER", "UNDO"} armature_object_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def update_armature_name(self, _context: Context) -> None: message = ( f"`{type(self).__qualname__}.armature_name` is deprecated" + " and will be removed in the next major release." + f" `Please use {type(self).__qualname__}.armature_object_name` instead." ) logger.warning(message) warnings.warn(message, DeprecationWarning, stacklevel=5) armature_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, update=update_armature_name, ) """`armature_name` is deprecated and will be removed in the next major release. Please use `armature_object_name` instead. """ author_index: IntProperty( # type: ignore[valid-type] options={"HIDDEN"}, min=0, ) def execute(self, context: Context) -> set[str]: if not self.armature_object_name and self.armature_name: self.armature_object_name = self.armature_name armature = context.blend_data.objects.get(self.armature_object_name) if armature is None or armature.type != "ARMATURE": return {"CANCELLED"} armature_data = armature.data if not isinstance(armature_data, Armature): return {"CANCELLED"} meta = get_armature_extension(armature_data).vrm1.meta if len(meta.authors) <= self.author_index: return {"CANCELLED"} new_index = (self.author_index + 1) % len(meta.authors) meta.authors.move(self.author_index, new_index) meta.active_author_index = new_index return {"FINISHED"} if TYPE_CHECKING: # This code is auto generated. # To regenerate, run the `uv run tools/property_typing.py` command. armature_object_name: str # type: ignore[no-redef] armature_name: str # type: ignore[no-redef] author_index: int # type: ignore[no-redef] class VRM_OT_add_vrm1_meta_reference(Operator): bl_idname = "vrm.add_vrm1_meta_reference" bl_label = "Add Reference" bl_description = "Add VRM 1.0 Meta Reference" bl_options: AbstractSet[str] = {"REGISTER", "UNDO"} armature_object_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def update_armature_name(self, _context: Context) -> None: message = ( f"`{type(self).__qualname__}.armature_name` is deprecated" + " and will be removed in the next major release." + f" `Please use {type(self).__qualname__}.armature_object_name` instead." ) logger.warning(message) warnings.warn(message, DeprecationWarning, stacklevel=5) armature_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, update=update_armature_name, ) """`armature_name` is deprecated and will be removed in the next major release. Please use `armature_object_name` instead. """ def execute(self, context: Context) -> set[str]: if not self.armature_object_name and self.armature_name: self.armature_object_name = self.armature_name armature = context.blend_data.objects.get(self.armature_object_name) if armature is None or armature.type != "ARMATURE": return {"CANCELLED"} armature_data = armature.data if not isinstance(armature_data, Armature): return {"CANCELLED"} meta = get_armature_extension(armature_data).vrm1.meta reference = meta.references.add() reference.value = "" meta.active_reference_index = len(meta.references) - 1 return {"FINISHED"} if TYPE_CHECKING: # This code is auto generated. # To regenerate, run the `uv run tools/property_typing.py` command. armature_object_name: str # type: ignore[no-redef] armature_name: str # type: ignore[no-redef] class VRM_OT_remove_vrm1_meta_reference(Operator): bl_idname = "vrm.remove_vrm1_meta_reference" bl_label = "Remove Reference" bl_description = "Remove VRM 1.0 Meta Reference" bl_options: AbstractSet[str] = {"REGISTER", "UNDO"} armature_object_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def update_armature_name(self, _context: Context) -> None: message = ( f"`{type(self).__qualname__}.armature_name` is deprecated" + " and will be removed in the next major release." + f" `Please use {type(self).__qualname__}.armature_object_name` instead." ) logger.warning(message) warnings.warn(message, DeprecationWarning, stacklevel=5) armature_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, update=update_armature_name, ) """`armature_name` is deprecated and will be removed in the next major release. Please use `armature_object_name` instead. """ reference_index: IntProperty( # type: ignore[valid-type] options={"HIDDEN"}, min=0, ) def execute(self, context: Context) -> set[str]: if not self.armature_object_name and self.armature_name: self.armature_object_name = self.armature_name armature = context.blend_data.objects.get(self.armature_object_name) if armature is None or armature.type != "ARMATURE": return {"CANCELLED"} armature_data = armature.data if not isinstance(armature_data, Armature): return {"CANCELLED"} meta = get_armature_extension(armature_data).vrm1.meta if len(meta.references) <= self.reference_index: return {"CANCELLED"} meta.references.remove(self.reference_index) meta.active_reference_index = min( meta.active_reference_index, max(0, len(meta.references) - 1), ) return {"FINISHED"} if TYPE_CHECKING: # This code is auto generated. # To regenerate, run the `uv run tools/property_typing.py` command. armature_object_name: str # type: ignore[no-redef] armature_name: str # type: ignore[no-redef] reference_index: int # type: ignore[no-redef] class VRM_OT_move_up_vrm1_meta_reference(Operator): bl_idname = "vrm.move_up_vrm1_meta_reference" bl_label = "Move Up Reference" bl_description = "Move Up VRM 1.0 Meta Reference" bl_options: AbstractSet[str] = {"REGISTER", "UNDO"} armature_object_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def update_armature_name(self, _context: Context) -> None: message = ( f"`{type(self).__qualname__}.armature_name` is deprecated" + " and will be removed in the next major release." + f" `Please use {type(self).__qualname__}.armature_object_name` instead." ) logger.warning(message) warnings.warn(message, DeprecationWarning, stacklevel=5) armature_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, update=update_armature_name, ) """`armature_name` is deprecated and will be removed in the next major release. Please use `armature_object_name` instead. """ reference_index: IntProperty( # type: ignore[valid-type] options={"HIDDEN"}, min=0, ) def execute(self, context: Context) -> set[str]: if not self.armature_object_name and self.armature_name: self.armature_object_name = self.armature_name armature = context.blend_data.objects.get(self.armature_object_name) if armature is None or armature.type != "ARMATURE": return {"CANCELLED"} armature_data = armature.data if not isinstance(armature_data, Armature): return {"CANCELLED"} meta = get_armature_extension(armature_data).vrm1.meta if len(meta.references) <= self.reference_index: return {"CANCELLED"} new_index = (self.reference_index - 1) % len(meta.references) meta.references.move(self.reference_index, new_index) meta.active_reference_index = new_index return {"FINISHED"} if TYPE_CHECKING: # This code is auto generated. # To regenerate, run the `uv run tools/property_typing.py` command. armature_object_name: str # type: ignore[no-redef] armature_name: str # type: ignore[no-redef] reference_index: int # type: ignore[no-redef] class VRM_OT_move_down_vrm1_meta_reference(Operator): bl_idname = "vrm.move_down_vrm1_meta_reference" bl_label = "Move Down Reference" bl_description = "Move Down VRM 1.0 Meta Reference" bl_options: AbstractSet[str] = {"REGISTER", "UNDO"} armature_object_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def update_armature_name(self, _context: Context) -> None: message = ( f"`{type(self).__qualname__}.armature_name` is deprecated" + " and will be removed in the next major release." + f" `Please use {type(self).__qualname__}.armature_object_name` instead." ) logger.warning(message) warnings.warn(message, DeprecationWarning, stacklevel=5) armature_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, update=update_armature_name, ) """`armature_name` is deprecated and will be removed in the next major release. Please use `armature_object_name` instead. """ reference_index: IntProperty( # type: ignore[valid-type] options={"HIDDEN"}, min=0, ) def execute(self, context: Context) -> set[str]: if not self.armature_object_name and self.armature_name: self.armature_object_name = self.armature_name armature = context.blend_data.objects.get(self.armature_object_name) if armature is None or armature.type != "ARMATURE": return {"CANCELLED"} armature_data = armature.data if not isinstance(armature_data, Armature): return {"CANCELLED"} meta = get_armature_extension(armature_data).vrm1.meta if len(meta.references) <= self.reference_index: return {"CANCELLED"} new_index = (self.reference_index + 1) % len(meta.references) meta.references.move(self.reference_index, new_index) meta.active_reference_index = new_index return {"FINISHED"} if TYPE_CHECKING: # This code is auto generated. # To regenerate, run the `uv run tools/property_typing.py` command. armature_object_name: str # type: ignore[no-redef] armature_name: str # type: ignore[no-redef] reference_index: int # type: ignore[no-redef] class VRM_OT_add_vrm1_expressions_custom_expression(Operator): bl_idname = "vrm.add_vrm1_expressions_custom_expression" bl_label = "Add Custom Expression" bl_description = "Add VRM 1.0 Custom Expression" bl_options: AbstractSet[str] = {"REGISTER", "UNDO"} armature_object_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def update_armature_name(self, _context: Context) -> None: message = ( f"`{type(self).__qualname__}.armature_name` is deprecated" + " and will be removed in the next major release." + f" `Please use {type(self).__qualname__}.armature_object_name` instead." ) logger.warning(message) warnings.warn(message, DeprecationWarning, stacklevel=5) armature_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, update=update_armature_name, ) """`armature_name` is deprecated and will be removed in the next major release. Please use `armature_object_name` instead. """ custom_expression_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def execute(self, context: Context) -> set[str]: if not self.armature_object_name and self.armature_name: self.armature_object_name = self.armature_name armature = context.blend_data.objects.get(self.armature_object_name) if armature is None or armature.type != "ARMATURE": return {"CANCELLED"} armature_data = armature.data if not isinstance(armature_data, Armature): return {"CANCELLED"} expressions = get_armature_extension(armature_data).vrm1.expressions new_last_custom_index = len(expressions.custom) custom_expression = expressions.custom.add() custom_expression.custom_name = self.custom_expression_name expressions.active_expression_ui_list_element_index = ( len(expressions.preset.name_to_expression_dict()) + new_last_custom_index ) return ops.vrm.update_vrm1_expression_ui_list_elements() if TYPE_CHECKING: # This code is auto generated. # To regenerate, run the `uv run tools/property_typing.py` command. armature_object_name: str # type: ignore[no-redef] armature_name: str # type: ignore[no-redef] custom_expression_name: str # type: ignore[no-redef] class VRM_OT_remove_vrm1_expressions_custom_expression(Operator): bl_idname = "vrm.remove_vrm1_expressions_custom_expression" bl_label = "Remove Custom Expression" bl_description = "Remove VRM 1.0 Custom Expression" bl_options: AbstractSet[str] = {"REGISTER", "UNDO"} armature_object_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def update_armature_name(self, _context: Context) -> None: message = ( f"`{type(self).__qualname__}.armature_name` is deprecated" + " and will be removed in the next major release." + f" `Please use {type(self).__qualname__}.armature_object_name` instead." ) logger.warning(message) warnings.warn(message, DeprecationWarning, stacklevel=5) armature_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, update=update_armature_name, ) """`armature_name` is deprecated and will be removed in the next major release. Please use `armature_object_name` instead. """ custom_expression_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def execute(self, context: Context) -> set[str]: if not self.armature_object_name and self.armature_name: self.armature_object_name = self.armature_name armature = context.blend_data.objects.get(self.armature_object_name) if armature is None or armature.type != "ARMATURE": return {"CANCELLED"} armature_data = armature.data if not isinstance(armature_data, Armature): return {"CANCELLED"} expressions = get_armature_extension(armature_data).vrm1.expressions for custom_index, custom_expression in enumerate( list(expressions.custom.values()) ): if custom_expression.custom_name == self.custom_expression_name: expressions.custom.remove(custom_index) expressions.active_expression_ui_list_element_index = min( expressions.active_expression_ui_list_element_index, len(expressions.all_name_to_expression_dict()) - 1, ) return ops.vrm.update_vrm1_expression_ui_list_elements() return {"CANCELLED"} if TYPE_CHECKING: # This code is auto generated. # To regenerate, run the `uv run tools/property_typing.py` command. armature_object_name: str # type: ignore[no-redef] armature_name: str # type: ignore[no-redef] custom_expression_name: str # type: ignore[no-redef] class VRM_OT_move_up_vrm1_expressions_custom_expression(Operator): bl_idname = "vrm.move_up_vrm1_expressions_custom_expression" bl_label = "Move Up Custom Expression" bl_description = "Move Up VRM 1.0 Custom Expression" bl_options: AbstractSet[str] = {"REGISTER", "UNDO"} armature_object_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def update_armature_name(self, _context: Context) -> None: message = ( f"`{type(self).__qualname__}.armature_name` is deprecated" + " and will be removed in the next major release." + f" `Please use {type(self).__qualname__}.armature_object_name` instead." ) logger.warning(message) warnings.warn(message, DeprecationWarning, stacklevel=5) armature_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, update=update_armature_name, ) """`armature_name` is deprecated and will be removed in the next major release. Please use `armature_object_name` instead. """ custom_expression_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def execute(self, context: Context) -> set[str]: if not self.armature_object_name and self.armature_name: self.armature_object_name = self.armature_name armature = context.blend_data.objects.get(self.armature_object_name) if armature is None or armature.type != "ARMATURE": return {"CANCELLED"} armature_data = armature.data if not isinstance(armature_data, Armature): return {"CANCELLED"} expressions = get_armature_extension(armature_data).vrm1.expressions expression_index = next( ( i for i, expression in enumerate(expressions.custom) if expression.custom_name == self.custom_expression_name ), None, ) if expression_index is None: return {"CANCELLED"} new_expression_index = (expression_index - 1) % len(expressions.custom) expressions.custom.move(expression_index, new_expression_index) expressions.active_expression_ui_list_element_index = ( len(expressions.preset.name_to_expression_dict()) + new_expression_index ) return {"FINISHED"} if TYPE_CHECKING: # This code is auto generated. # To regenerate, run the `uv run tools/property_typing.py` command. armature_object_name: str # type: ignore[no-redef] armature_name: str # type: ignore[no-redef] custom_expression_name: str # type: ignore[no-redef] class VRM_OT_move_down_vrm1_expressions_custom_expression(Operator): bl_idname = "vrm.move_down_vrm1_expressions_custom_expression" bl_label = "Move Down Custom Expression" bl_description = "Move Down VRM 1.0 Custom Expression" bl_options: AbstractSet[str] = {"REGISTER", "UNDO"} armature_object_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def update_armature_name(self, _context: Context) -> None: message = ( f"`{type(self).__qualname__}.armature_name` is deprecated" + " and will be removed in the next major release." + f" `Please use {type(self).__qualname__}.armature_object_name` instead." ) logger.warning(message) warnings.warn(message, DeprecationWarning, stacklevel=5) armature_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, update=update_armature_name, ) """`armature_name` is deprecated and will be removed in the next major release. Please use `armature_object_name` instead. """ custom_expression_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def execute(self, context: Context) -> set[str]: if not self.armature_object_name and self.armature_name: self.armature_object_name = self.armature_name armature = context.blend_data.objects.get(self.armature_object_name) if armature is None or armature.type != "ARMATURE": return {"CANCELLED"} armature_data = armature.data if not isinstance(armature_data, Armature): return {"CANCELLED"} expressions = get_armature_extension(armature_data).vrm1.expressions expression_index = next( ( i for i, expression in enumerate(expressions.custom) if expression.custom_name == self.custom_expression_name ), None, ) if expression_index is None: return {"CANCELLED"} new_expression_index = (expression_index + 1) % len(expressions.custom) expressions.custom.move(expression_index, new_expression_index) expressions.active_expression_ui_list_element_index = ( len(expressions.preset.name_to_expression_dict()) + new_expression_index ) return {"FINISHED"} if TYPE_CHECKING: # This code is auto generated. # To regenerate, run the `uv run tools/property_typing.py` command. armature_object_name: str # type: ignore[no-redef] armature_name: str # type: ignore[no-redef] custom_expression_name: str # type: ignore[no-redef] class VRM_OT_add_vrm1_first_person_mesh_annotation(Operator): bl_idname = "vrm.add_vrm1_first_person_mesh_annotation" bl_label = "Add Mesh Annotation" bl_description = "Add VRM 1.0 First Person Mesh Annotation" bl_options: AbstractSet[str] = {"REGISTER", "UNDO"} armature_object_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def update_armature_name(self, _context: Context) -> None: message = ( f"`{type(self).__qualname__}.armature_name` is deprecated" + " and will be removed in the next major release." + f" `Please use {type(self).__qualname__}.armature_object_name` instead." ) logger.warning(message) warnings.warn(message, DeprecationWarning, stacklevel=5) armature_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, update=update_armature_name, ) """`armature_name` is deprecated and will be removed in the next major release. Please use `armature_object_name` instead. """ def execute(self, context: Context) -> set[str]: if not self.armature_object_name and self.armature_name: self.armature_object_name = self.armature_name armature = context.blend_data.objects.get(self.armature_object_name) if armature is None or armature.type != "ARMATURE": return {"CANCELLED"} armature_data = armature.data if not isinstance(armature_data, Armature): return {"CANCELLED"} first_person = get_armature_extension(armature_data).vrm1.first_person first_person.mesh_annotations.add() first_person.active_mesh_annotation_index = ( len(first_person.mesh_annotations) - 1 ) return {"FINISHED"} if TYPE_CHECKING: # This code is auto generated. # To regenerate, run the `uv run tools/property_typing.py` command. armature_object_name: str # type: ignore[no-redef] armature_name: str # type: ignore[no-redef] class VRM_OT_remove_vrm1_first_person_mesh_annotation(Operator): bl_idname = "vrm.remove_vrm1_first_person_mesh_annotation" bl_label = "Remove Mesh Annotation" bl_description = "Remove VRM 1.0 First Person Mesh Annotation" bl_options: AbstractSet[str] = {"REGISTER", "UNDO"} armature_object_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def update_armature_name(self, _context: Context) -> None: message = ( f"`{type(self).__qualname__}.armature_name` is deprecated" + " and will be removed in the next major release." + f" `Please use {type(self).__qualname__}.armature_object_name` instead." ) logger.warning(message) warnings.warn(message, DeprecationWarning, stacklevel=5) armature_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, update=update_armature_name, ) """`armature_name` is deprecated and will be removed in the next major release. Please use `armature_object_name` instead. """ mesh_annotation_index: IntProperty( # type: ignore[valid-type] options={"HIDDEN"}, min=0, ) def execute(self, context: Context) -> set[str]: if not self.armature_object_name and self.armature_name: self.armature_object_name = self.armature_name armature = context.blend_data.objects.get(self.armature_object_name) if armature is None or armature.type != "ARMATURE": return {"CANCELLED"} armature_data = armature.data if not isinstance(armature_data, Armature): return {"CANCELLED"} first_person = get_armature_extension(armature_data).vrm1.first_person if len(first_person.mesh_annotations) <= self.mesh_annotation_index: return {"CANCELLED"} first_person.mesh_annotations.remove(self.mesh_annotation_index) first_person.active_mesh_annotation_index = min( first_person.active_mesh_annotation_index, max(0, len(first_person.mesh_annotations) - 1), ) return {"FINISHED"} if TYPE_CHECKING: # This code is auto generated. # To regenerate, run the `uv run tools/property_typing.py` command. armature_object_name: str # type: ignore[no-redef] armature_name: str # type: ignore[no-redef] mesh_annotation_index: int # type: ignore[no-redef] class VRM_OT_move_up_vrm1_first_person_mesh_annotation(Operator): bl_idname = "vrm.move_up_vrm1_first_person_mesh_annotation" bl_label = "Move Up Mesh Annotation" bl_description = "Move Up VRM 1.0 First Person Mesh Annotation" bl_options: AbstractSet[str] = {"REGISTER", "UNDO"} armature_object_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def update_armature_name(self, _context: Context) -> None: message = ( f"`{type(self).__qualname__}.armature_name` is deprecated" + " and will be removed in the next major release." + f" `Please use {type(self).__qualname__}.armature_object_name` instead." ) logger.warning(message) warnings.warn(message, DeprecationWarning, stacklevel=5) armature_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, update=update_armature_name, ) """`armature_name` is deprecated and will be removed in the next major release. Please use `armature_object_name` instead. """ mesh_annotation_index: IntProperty( # type: ignore[valid-type] options={"HIDDEN"}, min=0, ) def execute(self, context: Context) -> set[str]: if not self.armature_object_name and self.armature_name: self.armature_object_name = self.armature_name armature = context.blend_data.objects.get(self.armature_object_name) if armature is None or armature.type != "ARMATURE": return {"CANCELLED"} armature_data = armature.data if not isinstance(armature_data, Armature): return {"CANCELLED"} first_person = get_armature_extension(armature_data).vrm1.first_person if len(first_person.mesh_annotations) <= self.mesh_annotation_index: return {"CANCELLED"} new_index = (self.mesh_annotation_index - 1) % len( first_person.mesh_annotations ) first_person.mesh_annotations.move(self.mesh_annotation_index, new_index) first_person.active_mesh_annotation_index = new_index return {"FINISHED"} if TYPE_CHECKING: # This code is auto generated. # To regenerate, run the `uv run tools/property_typing.py` command. armature_object_name: str # type: ignore[no-redef] armature_name: str # type: ignore[no-redef] mesh_annotation_index: int # type: ignore[no-redef] class VRM_OT_move_down_vrm1_first_person_mesh_annotation(Operator): bl_idname = "vrm.move_down_vrm1_first_person_mesh_annotation" bl_label = "Move Down Mesh Annotation" bl_description = "Move Down VRM 1.0 First Person Mesh Annotation" bl_options: AbstractSet[str] = {"REGISTER", "UNDO"} armature_object_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def update_armature_name(self, _context: Context) -> None: message = ( f"`{type(self).__qualname__}.armature_name` is deprecated" + " and will be removed in the next major release." + f" `Please use {type(self).__qualname__}.armature_object_name` instead." ) logger.warning(message) warnings.warn(message, DeprecationWarning, stacklevel=5) armature_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, update=update_armature_name, ) """`armature_name` is deprecated and will be removed in the next major release. Please use `armature_object_name` instead. """ mesh_annotation_index: IntProperty( # type: ignore[valid-type] options={"HIDDEN"}, min=0, ) def execute(self, context: Context) -> set[str]: if not self.armature_object_name and self.armature_name: self.armature_object_name = self.armature_name armature = context.blend_data.objects.get(self.armature_object_name) if armature is None or armature.type != "ARMATURE": return {"CANCELLED"} armature_data = armature.data if not isinstance(armature_data, Armature): return {"CANCELLED"} first_person = get_armature_extension(armature_data).vrm1.first_person if len(first_person.mesh_annotations) <= self.mesh_annotation_index: return {"CANCELLED"} new_index = (self.mesh_annotation_index + 1) % len( first_person.mesh_annotations ) first_person.mesh_annotations.move(self.mesh_annotation_index, new_index) first_person.active_mesh_annotation_index = new_index return {"FINISHED"} if TYPE_CHECKING: # This code is auto generated. # To regenerate, run the `uv run tools/property_typing.py` command. armature_object_name: str # type: ignore[no-redef] armature_name: str # type: ignore[no-redef] mesh_annotation_index: int # type: ignore[no-redef] class VRM_OT_add_vrm1_expression_morph_target_bind(Operator): bl_idname = "vrm.add_vrm1_expression_morph_target_bind" bl_label = "Add Morph Target Bind" bl_description = "Add VRM 1.0 Expression Morph Target Bind" bl_options: AbstractSet[str] = {"REGISTER", "UNDO"} armature_object_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def update_armature_name(self, _context: Context) -> None: message = ( f"`{type(self).__qualname__}.armature_name` is deprecated" + " and will be removed in the next major release." + f" `Please use {type(self).__qualname__}.armature_object_name` instead." ) logger.warning(message) warnings.warn(message, DeprecationWarning, stacklevel=5) armature_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, update=update_armature_name, ) """`armature_name` is deprecated and will be removed in the next major release. Please use `armature_object_name` instead. """ expression_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def execute(self, context: Context) -> set[str]: if not self.armature_object_name and self.armature_name: self.armature_object_name = self.armature_name armature = context.blend_data.objects.get(self.armature_object_name) if armature is None or armature.type != "ARMATURE": return {"CANCELLED"} armature_data = armature.data if not isinstance(armature_data, Armature): return {"CANCELLED"} expressions = get_armature_extension(armature_data).vrm1.expressions expression = expressions.all_name_to_expression_dict().get(self.expression_name) if expression is None: return {"CANCELLED"} expression.morph_target_binds.add() expression.active_morph_target_bind_index = ( len(expression.morph_target_binds) - 1 ) return {"FINISHED"} if TYPE_CHECKING: # This code is auto generated. # To regenerate, run the `uv run tools/property_typing.py` command. armature_object_name: str # type: ignore[no-redef] armature_name: str # type: ignore[no-redef] expression_name: str # type: ignore[no-redef] class VRM_OT_remove_vrm1_expression_morph_target_bind(Operator): bl_idname = "vrm.remove_vrm1_expression_morph_target_bind" bl_label = "Remove Morph Target Bind" bl_description = "Remove VRM 1.0 Expression Morph Target Bind" bl_options: AbstractSet[str] = {"REGISTER", "UNDO"} armature_object_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def update_armature_name(self, _context: Context) -> None: message = ( f"`{type(self).__qualname__}.armature_name` is deprecated" + " and will be removed in the next major release." + f" `Please use {type(self).__qualname__}.armature_object_name` instead." ) logger.warning(message) warnings.warn(message, DeprecationWarning, stacklevel=5) armature_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, update=update_armature_name, ) """`armature_name` is deprecated and will be removed in the next major release. Please use `armature_object_name` instead. """ expression_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) bind_index: IntProperty( # type: ignore[valid-type] options={"HIDDEN"}, min=0, ) def execute(self, context: Context) -> set[str]: if not self.armature_object_name and self.armature_name: self.armature_object_name = self.armature_name armature = context.blend_data.objects.get(self.armature_object_name) if armature is None or armature.type != "ARMATURE": return {"CANCELLED"} armature_data = armature.data if not isinstance(armature_data, Armature): return {"CANCELLED"} expressions = get_armature_extension(armature_data).vrm1.expressions expression = expressions.all_name_to_expression_dict().get(self.expression_name) if expression is None: return {"CANCELLED"} if len(expression.morph_target_binds) <= self.bind_index: return {"CANCELLED"} expression.morph_target_binds.remove(self.bind_index) expression.active_morph_target_bind_index = min( expression.active_morph_target_bind_index, max(0, len(expression.morph_target_binds) - 1), ) return {"FINISHED"} if TYPE_CHECKING: # This code is auto generated. # To regenerate, run the `uv run tools/property_typing.py` command. armature_object_name: str # type: ignore[no-redef] armature_name: str # type: ignore[no-redef] expression_name: str # type: ignore[no-redef] bind_index: int # type: ignore[no-redef] class VRM_OT_move_up_vrm1_expression_morph_target_bind(Operator): bl_idname = "vrm.move_up_vrm1_expression_morph_target_bind" bl_label = "Move Up Morph Target Bind" bl_description = "Move Up VRM 1.0 Expression Morph Target Bind" bl_options: AbstractSet[str] = {"REGISTER", "UNDO"} armature_object_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def update_armature_name(self, _context: Context) -> None: message = ( f"`{type(self).__qualname__}.armature_name` is deprecated" + " and will be removed in the next major release." + f" `Please use {type(self).__qualname__}.armature_object_name` instead." ) logger.warning(message) warnings.warn(message, DeprecationWarning, stacklevel=5) armature_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, update=update_armature_name, ) """`armature_name` is deprecated and will be removed in the next major release. Please use `armature_object_name` instead. """ expression_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) bind_index: IntProperty( # type: ignore[valid-type] options={"HIDDEN"}, min=0, ) def execute(self, context: Context) -> set[str]: if not self.armature_object_name and self.armature_name: self.armature_object_name = self.armature_name armature = context.blend_data.objects.get(self.armature_object_name) if armature is None or armature.type != "ARMATURE": return {"CANCELLED"} armature_data = armature.data if not isinstance(armature_data, Armature): return {"CANCELLED"} expressions = get_armature_extension(armature_data).vrm1.expressions expression = expressions.all_name_to_expression_dict().get(self.expression_name) if expression is None: return {"CANCELLED"} if len(expression.morph_target_binds) <= self.bind_index: return {"CANCELLED"} new_bind_index = (self.bind_index - 1) % len(expression.morph_target_binds) expression.morph_target_binds.move(self.bind_index, new_bind_index) expression.active_morph_target_bind_index = new_bind_index return {"FINISHED"} if TYPE_CHECKING: # This code is auto generated. # To regenerate, run the `uv run tools/property_typing.py` command. armature_object_name: str # type: ignore[no-redef] armature_name: str # type: ignore[no-redef] expression_name: str # type: ignore[no-redef] bind_index: int # type: ignore[no-redef] class VRM_OT_move_down_vrm1_expression_morph_target_bind(Operator): bl_idname = "vrm.move_down_vrm1_expression_morph_target_bind" bl_label = "Move Down Morph Target Bind" bl_description = "Move Down VRM 1.0 Expression Morph Target Bind" bl_options: AbstractSet[str] = {"REGISTER", "UNDO"} armature_object_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def update_armature_name(self, _context: Context) -> None: message = ( f"`{type(self).__qualname__}.armature_name` is deprecated" + " and will be removed in the next major release." + f" `Please use {type(self).__qualname__}.armature_object_name` instead." ) logger.warning(message) warnings.warn(message, DeprecationWarning, stacklevel=5) armature_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, update=update_armature_name, ) """`armature_name` is deprecated and will be removed in the next major release. Please use `armature_object_name` instead. """ expression_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) bind_index: IntProperty( # type: ignore[valid-type] options={"HIDDEN"}, min=0, ) def execute(self, context: Context) -> set[str]: if not self.armature_object_name and self.armature_name: self.armature_object_name = self.armature_name armature = context.blend_data.objects.get(self.armature_object_name) if armature is None or armature.type != "ARMATURE": return {"CANCELLED"} armature_data = armature.data if not isinstance(armature_data, Armature): return {"CANCELLED"} expressions = get_armature_extension(armature_data).vrm1.expressions expression = expressions.all_name_to_expression_dict().get(self.expression_name) if expression is None: return {"CANCELLED"} if len(expression.morph_target_binds) <= self.bind_index: return {"CANCELLED"} new_bind_index = (self.bind_index + 1) % len(expression.morph_target_binds) expression.morph_target_binds.move(self.bind_index, new_bind_index) expression.active_morph_target_bind_index = new_bind_index return {"FINISHED"} if TYPE_CHECKING: # This code is auto generated. # To regenerate, run the `uv run tools/property_typing.py` command. armature_object_name: str # type: ignore[no-redef] armature_name: str # type: ignore[no-redef] expression_name: str # type: ignore[no-redef] bind_index: int # type: ignore[no-redef] class VRM_OT_restore_vrm1_expression_morph_target_bind_object(Operator): bl_idname = "vrm.restore_vrm1_expression_morph_target_bind_object" bl_label = "Restore Shape Key Assignments" bl_description = "Restore VRM 1.0 Expression Morph Target Bind Object Assignments" bl_options: AbstractSet[str] = {"REGISTER", "UNDO"} armature_object_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def execute(self, context: Context) -> set[str]: armature = context.blend_data.objects.get(self.armature_object_name) if armature is None or armature.type != "ARMATURE": return {"CANCELLED"} armature_data = armature.data if not isinstance(armature_data, Armature): return {"CANCELLED"} expressions = get_armature_extension(armature_data).vrm1.expressions expressions.restore_expression_morph_target_bind_object_assignments(context) return {"FINISHED"} if TYPE_CHECKING: # This code is auto generated. # To regenerate, run the `uv run tools/property_typing.py` command. armature_object_name: str # type: ignore[no-redef] class VRM_OT_add_vrm1_expression_material_color_bind(Operator): bl_idname = "vrm.add_vrm1_expression_material_color_bind" bl_label = "Add Material Color Bind" bl_description = "Add VRM 1.0 Expression Material Value Bind" bl_options: AbstractSet[str] = {"REGISTER", "UNDO"} armature_object_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def update_armature_name(self, _context: Context) -> None: message = ( f"`{type(self).__qualname__}.armature_name` is deprecated" + " and will be removed in the next major release." + f" `Please use {type(self).__qualname__}.armature_object_name` instead." ) logger.warning(message) warnings.warn(message, DeprecationWarning, stacklevel=5) armature_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, update=update_armature_name, ) """`armature_name` is deprecated and will be removed in the next major release. Please use `armature_object_name` instead. """ expression_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def execute(self, context: Context) -> set[str]: if not self.armature_object_name and self.armature_name: self.armature_object_name = self.armature_name armature = context.blend_data.objects.get(self.armature_object_name) if armature is None or armature.type != "ARMATURE": return {"CANCELLED"} armature_data = armature.data if not isinstance(armature_data, Armature): return {"CANCELLED"} ext = get_armature_extension(armature_data) expression = ext.vrm1.expressions.all_name_to_expression_dict().get( self.expression_name ) if expression is None: return {"CANCELLED"} expression.material_color_binds.add() expression.active_material_color_bind_index = ( len(expression.material_color_binds) - 1 ) return {"FINISHED"} if TYPE_CHECKING: # This code is auto generated. # To regenerate, run the `uv run tools/property_typing.py` command. armature_object_name: str # type: ignore[no-redef] armature_name: str # type: ignore[no-redef] expression_name: str # type: ignore[no-redef] class VRM_OT_remove_vrm1_expression_material_color_bind(Operator): bl_idname = "vrm.remove_vrm1_expression_material_color_bind" bl_label = "Remove Material Color Bind" bl_description = "Remove VRM 1.0 Expression Material Color Bind" bl_options: AbstractSet[str] = {"REGISTER", "UNDO"} armature_object_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def update_armature_name(self, _context: Context) -> None: message = ( f"`{type(self).__qualname__}.armature_name` is deprecated" + " and will be removed in the next major release." + f" `Please use {type(self).__qualname__}.armature_object_name` instead." ) logger.warning(message) warnings.warn(message, DeprecationWarning, stacklevel=5) armature_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, update=update_armature_name, ) """`armature_name` is deprecated and will be removed in the next major release. Please use `armature_object_name` instead. """ expression_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) bind_index: IntProperty( # type: ignore[valid-type] options={"HIDDEN"}, min=0, ) def execute(self, context: Context) -> set[str]: if not self.armature_object_name and self.armature_name: self.armature_object_name = self.armature_name armature = context.blend_data.objects.get(self.armature_object_name) if armature is None or armature.type != "ARMATURE": return {"CANCELLED"} armature_data = armature.data if not isinstance(armature_data, Armature): return {"CANCELLED"} expressions = get_armature_extension(armature_data).vrm1.expressions expression = expressions.all_name_to_expression_dict().get(self.expression_name) if expression is None: return {"CANCELLED"} if len(expression.material_color_binds) <= self.bind_index: return {"CANCELLED"} expression.material_color_binds.remove(self.bind_index) expression.active_material_color_bind_index = min( expression.active_material_color_bind_index, max(0, len(expression.material_color_binds) - 1), ) return {"FINISHED"} if TYPE_CHECKING: # This code is auto generated. # To regenerate, run the `uv run tools/property_typing.py` command. armature_object_name: str # type: ignore[no-redef] armature_name: str # type: ignore[no-redef] expression_name: str # type: ignore[no-redef] bind_index: int # type: ignore[no-redef] class VRM_OT_move_up_vrm1_expression_material_color_bind(Operator): bl_idname = "vrm.move_up_vrm1_expression_material_color_bind" bl_label = "Move Up Material Color Bind" bl_description = "Move Up VRM 1.0 Expression Material Color Bind" bl_options: AbstractSet[str] = {"REGISTER", "UNDO"} armature_object_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def update_armature_name(self, _context: Context) -> None: message = ( f"`{type(self).__qualname__}.armature_name` is deprecated" + " and will be removed in the next major release." + f" `Please use {type(self).__qualname__}.armature_object_name` instead." ) logger.warning(message) warnings.warn(message, DeprecationWarning, stacklevel=5) armature_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, update=update_armature_name, ) """`armature_name` is deprecated and will be removed in the next major release. Please use `armature_object_name` instead. """ expression_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) bind_index: IntProperty( # type: ignore[valid-type] options={"HIDDEN"}, min=0, ) def execute(self, context: Context) -> set[str]: if not self.armature_object_name and self.armature_name: self.armature_object_name = self.armature_name armature = context.blend_data.objects.get(self.armature_object_name) if armature is None or armature.type != "ARMATURE": return {"CANCELLED"} armature_data = armature.data if not isinstance(armature_data, Armature): return {"CANCELLED"} expressions = get_armature_extension(armature_data).vrm1.expressions expression = expressions.all_name_to_expression_dict().get(self.expression_name) if expression is None: return {"CANCELLED"} if len(expression.material_color_binds) <= self.bind_index: return {"CANCELLED"} new_bind_index = (self.bind_index - 1) % len(expression.material_color_binds) expression.material_color_binds.move(self.bind_index, new_bind_index) expression.active_material_color_bind_index = new_bind_index return {"FINISHED"} if TYPE_CHECKING: # This code is auto generated. # To regenerate, run the `uv run tools/property_typing.py` command. armature_object_name: str # type: ignore[no-redef] armature_name: str # type: ignore[no-redef] expression_name: str # type: ignore[no-redef] bind_index: int # type: ignore[no-redef] class VRM_OT_move_down_vrm1_expression_material_color_bind(Operator): bl_idname = "vrm.move_down_vrm1_expression_material_color_bind" bl_label = "Move Down Material Color Bind" bl_description = "Move Down VRM 1.0 Expression Material Color Bind" bl_options: AbstractSet[str] = {"REGISTER", "UNDO"} armature_object_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def update_armature_name(self, _context: Context) -> None: message = ( f"`{type(self).__qualname__}.armature_name` is deprecated" + " and will be removed in the next major release." + f" `Please use {type(self).__qualname__}.armature_object_name` instead." ) logger.warning(message) warnings.warn(message, DeprecationWarning, stacklevel=5) armature_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, update=update_armature_name, ) """`armature_name` is deprecated and will be removed in the next major release. Please use `armature_object_name` instead. """ expression_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) bind_index: IntProperty( # type: ignore[valid-type] options={"HIDDEN"}, min=0, ) def execute(self, context: Context) -> set[str]: if not self.armature_object_name and self.armature_name: self.armature_object_name = self.armature_name armature = context.blend_data.objects.get(self.armature_object_name) if armature is None or armature.type != "ARMATURE": return {"CANCELLED"} armature_data = armature.data if not isinstance(armature_data, Armature): return {"CANCELLED"} expressions = get_armature_extension(armature_data).vrm1.expressions expression = expressions.all_name_to_expression_dict().get(self.expression_name) if expression is None: return {"CANCELLED"} if len(expression.material_color_binds) <= self.bind_index: return {"CANCELLED"} new_bind_index = (self.bind_index + 1) % len(expression.material_color_binds) expression.material_color_binds.move(self.bind_index, new_bind_index) expression.active_material_color_bind_index = new_bind_index return {"FINISHED"} if TYPE_CHECKING: # This code is auto generated. # To regenerate, run the `uv run tools/property_typing.py` command. armature_object_name: str # type: ignore[no-redef] armature_name: str # type: ignore[no-redef] expression_name: str # type: ignore[no-redef] bind_index: int # type: ignore[no-redef] class VRM_OT_add_vrm1_expression_texture_transform_bind(Operator): bl_idname = "vrm.add_vrm1_expression_texture_transform_bind" bl_label = "Add Texture Transform Bind" bl_description = "Add VRM 1.0 Expression Texture Transform Bind" bl_options: AbstractSet[str] = {"REGISTER", "UNDO"} armature_object_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def update_armature_name(self, _context: Context) -> None: message = ( f"`{type(self).__qualname__}.armature_name` is deprecated" + " and will be removed in the next major release." + f" `Please use {type(self).__qualname__}.armature_object_name` instead." ) logger.warning(message) warnings.warn(message, DeprecationWarning, stacklevel=5) armature_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, update=update_armature_name, ) """`armature_name` is deprecated and will be removed in the next major release. Please use `armature_object_name` instead. """ expression_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def execute(self, context: Context) -> set[str]: if not self.armature_object_name and self.armature_name: self.armature_object_name = self.armature_name armature = context.blend_data.objects.get(self.armature_object_name) if armature is None or armature.type != "ARMATURE": return {"CANCELLED"} armature_data = armature.data if not isinstance(armature_data, Armature): return {"CANCELLED"} expressions = get_armature_extension(armature_data).vrm1.expressions expression = expressions.all_name_to_expression_dict().get(self.expression_name) if expression is None: return {"CANCELLED"} expression.texture_transform_binds.add() expression.active_texture_transform_bind_index = ( len(expression.texture_transform_binds) - 1 ) return {"FINISHED"} if TYPE_CHECKING: # This code is auto generated. # To regenerate, run the `uv run tools/property_typing.py` command. armature_object_name: str # type: ignore[no-redef] armature_name: str # type: ignore[no-redef] expression_name: str # type: ignore[no-redef] class VRM_OT_remove_vrm1_expression_texture_transform_bind(Operator): bl_idname = "vrm.remove_vrm1_expression_texture_transform_bind" bl_label = "Remove Texture Transform Bind" bl_description = "Remove VRM 1.0 Expression Texture Transform Bind" bl_options: AbstractSet[str] = {"REGISTER", "UNDO"} armature_object_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def update_armature_name(self, _context: Context) -> None: message = ( f"`{type(self).__qualname__}.armature_name` is deprecated" + " and will be removed in the next major release." + f" `Please use {type(self).__qualname__}.armature_object_name` instead." ) logger.warning(message) warnings.warn(message, DeprecationWarning, stacklevel=5) armature_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, update=update_armature_name, ) """`armature_name` is deprecated and will be removed in the next major release. Please use `armature_object_name` instead. """ expression_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) bind_index: IntProperty( # type: ignore[valid-type] options={"HIDDEN"}, min=0, ) def execute(self, context: Context) -> set[str]: if not self.armature_object_name and self.armature_name: self.armature_object_name = self.armature_name armature = context.blend_data.objects.get(self.armature_object_name) if armature is None or armature.type != "ARMATURE": return {"CANCELLED"} armature_data = armature.data if not isinstance(armature_data, Armature): return {"CANCELLED"} expressions = get_armature_extension(armature_data).vrm1.expressions expression = expressions.all_name_to_expression_dict().get(self.expression_name) if expression is None: return {"CANCELLED"} if len(expression.texture_transform_binds) <= self.bind_index: return {"CANCELLED"} expression.texture_transform_binds.remove(self.bind_index) expression.active_texture_transform_bind_index = min( expression.active_texture_transform_bind_index, max(0, len(expression.texture_transform_binds) - 1), ) return {"FINISHED"} if TYPE_CHECKING: # This code is auto generated. # To regenerate, run the `uv run tools/property_typing.py` command. armature_object_name: str # type: ignore[no-redef] armature_name: str # type: ignore[no-redef] expression_name: str # type: ignore[no-redef] bind_index: int # type: ignore[no-redef] class VRM_OT_move_up_vrm1_expression_texture_transform_bind(Operator): bl_idname = "vrm.move_up_vrm1_expression_texture_transform_bind" bl_label = "Move Up Texture Transform Bind" bl_description = "Move Up VRM 1.0 Expression Texture Transform Bind" bl_options: AbstractSet[str] = {"REGISTER", "UNDO"} armature_object_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def update_armature_name(self, _context: Context) -> None: message = ( f"`{type(self).__qualname__}.armature_name` is deprecated" + " and will be removed in the next major release." + f" `Please use {type(self).__qualname__}.armature_object_name` instead." ) logger.warning(message) warnings.warn(message, DeprecationWarning, stacklevel=5) armature_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, update=update_armature_name, ) """`armature_name` is deprecated and will be removed in the next major release. Please use `armature_object_name` instead. """ expression_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) bind_index: IntProperty( # type: ignore[valid-type] options={"HIDDEN"}, min=0, ) def execute(self, context: Context) -> set[str]: if not self.armature_object_name and self.armature_name: self.armature_object_name = self.armature_name armature = context.blend_data.objects.get(self.armature_object_name) if armature is None or armature.type != "ARMATURE": return {"CANCELLED"} armature_data = armature.data if not isinstance(armature_data, Armature): return {"CANCELLED"} expressions = get_armature_extension(armature_data).vrm1.expressions expression = expressions.all_name_to_expression_dict().get(self.expression_name) if expression is None: return {"CANCELLED"} if len(expression.texture_transform_binds) <= self.bind_index: return {"CANCELLED"} new_bind_index = (self.bind_index - 1) % len(expression.texture_transform_binds) expression.texture_transform_binds.move(self.bind_index, new_bind_index) expression.active_texture_transform_bind_index = new_bind_index return {"FINISHED"} if TYPE_CHECKING: # This code is auto generated. # To regenerate, run the `uv run tools/property_typing.py` command. armature_object_name: str # type: ignore[no-redef] armature_name: str # type: ignore[no-redef] expression_name: str # type: ignore[no-redef] bind_index: int # type: ignore[no-redef] class VRM_OT_move_down_vrm1_expression_texture_transform_bind(Operator): bl_idname = "vrm.move_down_vrm1_expression_texture_transform_bind" bl_label = "Move Down Morph Target Bind" bl_description = "Move Down VRM 1.0 Expression Morph Target Bind" bl_options: AbstractSet[str] = {"REGISTER", "UNDO"} armature_object_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def update_armature_name(self, _context: Context) -> None: message = ( f"`{type(self).__qualname__}.armature_name` is deprecated" + " and will be removed in the next major release." + f" `Please use {type(self).__qualname__}.armature_object_name` instead." ) logger.warning(message) warnings.warn(message, DeprecationWarning, stacklevel=5) armature_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, update=update_armature_name, ) """`armature_name` is deprecated and will be removed in the next major release. Please use `armature_object_name` instead. """ expression_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) bind_index: IntProperty( # type: ignore[valid-type] options={"HIDDEN"}, min=0, ) def execute(self, context: Context) -> set[str]: if not self.armature_object_name and self.armature_name: self.armature_object_name = self.armature_name armature = context.blend_data.objects.get(self.armature_object_name) if armature is None or armature.type != "ARMATURE": return {"CANCELLED"} armature_data = armature.data if not isinstance(armature_data, Armature): return {"CANCELLED"} expressions = get_armature_extension(armature_data).vrm1.expressions expression = expressions.all_name_to_expression_dict().get(self.expression_name) if expression is None: return {"CANCELLED"} if len(expression.texture_transform_binds) <= self.bind_index: return {"CANCELLED"} new_bind_index = (self.bind_index + 1) % len(expression.texture_transform_binds) expression.texture_transform_binds.move(self.bind_index, new_bind_index) expression.active_texture_transform_bind_index = new_bind_index return {"FINISHED"} if TYPE_CHECKING: # This code is auto generated. # To regenerate, run the `uv run tools/property_typing.py` command. armature_object_name: str # type: ignore[no-redef] armature_name: str # type: ignore[no-redef] expression_name: str # type: ignore[no-redef] bind_index: int # type: ignore[no-redef] vrm0_human_bone_name_to_vrm1_human_bone_name: dict[Vrm0HumanBoneName, HumanBoneName] = { specification.vrm0_name: specification.name for specification in HumanBoneSpecifications.all_human_bones } class VRM_OT_assign_vrm1_humanoid_human_bones_automatically(Operator): bl_idname = "vrm.assign_vrm1_humanoid_human_bones_automatically" bl_label = "Automatic Bone Assignment" bl_description = "Assign VRM 1.0 Humanoid Human Bones" bl_options: AbstractSet[str] = {"REGISTER", "UNDO"} armature_object_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def update_armature_name(self, _context: Context) -> None: message = ( f"`{type(self).__qualname__}.armature_name` is deprecated" + " and will be removed in the next major release." + f" `Please use {type(self).__qualname__}.armature_object_name` instead." ) logger.warning(message) warnings.warn(message, DeprecationWarning, stacklevel=5) armature_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, update=update_armature_name, ) """`armature_name` is deprecated and will be removed in the next major release. Please use `armature_object_name` instead. """ def execute(self, context: Context) -> set[str]: if not self.armature_object_name and self.armature_name: self.armature_object_name = self.armature_name armature = context.blend_data.objects.get(self.armature_object_name) if armature is None or armature.type != "ARMATURE": return {"CANCELLED"} armature_data = armature.data if not isinstance(armature_data, Armature): return {"CANCELLED"} Vrm1HumanBonesPropertyGroup.fixup_human_bones(armature) Vrm1HumanBonesPropertyGroup.update_all_node_candidates( context, armature_data.name ) human_bones = get_armature_extension(armature_data).vrm1.humanoid.human_bones human_bone_name_to_human_bone = human_bones.human_bone_name_to_human_bone() bones = armature_data.bones Vrm0HumanoidPropertyGroup.fixup_human_bones(armature) Vrm0HumanoidPropertyGroup.update_all_node_candidates( context, armature_data.name ) vrm0_humanoid = get_armature_extension(armature_data).vrm0.humanoid if vrm0_humanoid.all_required_bones_are_assigned(): for vrm0_human_bone in vrm0_humanoid.human_bones: if ( vrm0_human_bone.node.bone_name not in vrm0_human_bone.node_candidates ): continue vrm0_name = Vrm0HumanBoneName.from_str(vrm0_human_bone.bone) if not vrm0_name: logger.error("Invalid VRM0 bone name str: %s", vrm0_human_bone.bone) continue vrm1_name = vrm0_human_bone_name_to_vrm1_human_bone_name.get(vrm0_name) if vrm1_name is None: logger.error("Invalid VRM0 bone name: %s", vrm0_name) continue human_bone = human_bone_name_to_human_bone.get(vrm1_name) if not human_bone: continue if vrm0_human_bone.node.bone_name not in human_bone.node_candidates: continue human_bone.node.bone_name = vrm0_human_bone.node.bone_name for ( bone_name, specification, ) in create_human_bone_mapping(armature).items(): bone = bones.get(bone_name) if not bone: continue for search_name, human_bone in human_bone_name_to_human_bone.items(): if ( specification.name != search_name or human_bone.node.bone_name in human_bone.node_candidates or bone_name not in human_bone.node_candidates ): continue human_bone.node.bone_name = bone_name break Vrm1HumanBonesPropertyGroup.update_all_node_candidates( context, armature_data.name, force=True ) return {"FINISHED"} if TYPE_CHECKING: # This code is auto generated. # To regenerate, run the `uv run tools/property_typing.py` command. armature_object_name: str # type: ignore[no-redef] armature_name: str # type: ignore[no-redef] class VRM_OT_update_vrm1_expression_ui_list_elements(Operator): bl_idname = "vrm.update_vrm1_expression_ui_list_elements" bl_label = "Update VRM 1.0 Expression UI list Elements" bl_options: AbstractSet[str] = {"REGISTER", "UNDO"} def execute(self, context: Context) -> set[str]: for armature in context.blend_data.armatures: expressions = get_armature_extension(armature).vrm1.expressions # Set the number of elements equal to the number of elements wanted to show # in the UIlist. ui_len = len(expressions.expression_ui_list_elements) all_len = len(expressions.all_name_to_expression_dict()) if ui_len == all_len: continue if ui_len > all_len: for _ in range(ui_len - all_len): expressions.expression_ui_list_elements.remove(0) if all_len > ui_len: for _ in range(all_len - ui_len): expressions.expression_ui_list_elements.add() return {"FINISHED"} class TextureTransformBind(Protocol): material: Material scale: tuple[float, float] offset: tuple[float, float] class VRM_OT_refresh_vrm1_expression_texture_transform_bind_preview(Operator): bl_idname = "vrm.refresh_vrm1_expression_texture_transform_bind_preview" bl_label = "Refresh Texture Transform Bind Preview" bl_description = "Refresh VRM 1.0 Expression Texture Transform Bind Preview" bl_options: AbstractSet[str] = {"REGISTER", "UNDO"} armature_object_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, ) def update_armature_name(self, _context: Context) -> None: message = ( f"`{type(self).__qualname__}.armature_name` is deprecated" + " and will be removed in the next major release." + f" `Please use {type(self).__qualname__}.armature_object_name` instead." ) logger.warning(message) warnings.warn(message, DeprecationWarning, stacklevel=5) armature_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"}, update=update_armature_name, ) """`armature_name` is deprecated and will be removed in the next major release. Please use `armature_object_name` instead. """ expression_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"} ) preset_name_mapping: ClassVar[dict[str, str]] = { "neutral": "neutral", "aa": "aa", "ih": "ih", "ou": "ou", "ee": "ee", "oh": "oh", "blink": "blink", "joy": "happy", "angry": "angry", "sorrow": "sad", "fun": "surprised", "relaxed": "relaxed", "blinkLeft": "blink_left", "blinkRight": "blink_right", "lookUp": "look_up", "lookDown": "look_down", "lookLeft": "look_left", "lookRight": "look_right", } def execute(self, context: Context) -> set[str]: if not self.armature_object_name and self.armature_name: self.armature_object_name = self.armature_name armature = context.blend_data.objects.get(self.armature_object_name) if armature is None or armature.type != "ARMATURE": return {"CANCELLED"} armature_data = armature.data if not isinstance(armature_data, bpy.types.Armature): return {"CANCELLED"} all_expressions = self.get_all_expressions() materials_to_update: set[Material] = set() expression = None for expression, _expr_type, _expr_name in all_expressions: for bind in expression.texture_transform_binds: if bind.material: materials_to_update.add(bind.material) if not expression: return {"FINISHED"} for material in materials_to_update: self.setup_uv_offset_nodes(context, armature, material, all_expressions) new_item = expression.materials_to_update.add() new_item.material = material material.update_tag() return {"FINISHED"} def get_all_expressions(self) -> list[tuple[Vrm1ExpressionPropertyGroup, str, str]]: if not self.armature_object_name and self.armature_name: self.armature_object_name = self.armature_name armature = bpy.data.objects.get(self.armature_object_name) if not armature or armature.type != "ARMATURE": return [] armature_data = armature.data if not isinstance(armature_data, Armature): return [] ext = get_armature_extension(armature_data) expressions = ext.vrm1.expressions all_expressions: list[tuple[Vrm1ExpressionPropertyGroup, str, str]] = [] for preset_name in self.preset_name_mapping.values(): preset_expr = getattr(expressions.preset, preset_name, None) if preset_expr: all_expressions.append((preset_expr, "preset", preset_name)) for i, custom_expr in enumerate(expressions.custom): all_expressions.append((custom_expr, "custom", str(i))) return all_expressions def setup_uv_offset_nodes( self, context: Context, armature: Object, material: Material, all_expressions: list[tuple[Vrm1ExpressionPropertyGroup, str, str]], ) -> None: if bpy.app.version < (5, 0, 0) and not material.use_nodes: material.use_nodes = True node_tree = material.node_tree if not isinstance(node_tree, bpy.types.ShaderNodeTree): return self.remove_existing_nodes(node_tree) # Create a new node group group_name = f"VRM_TextureTransform_{material.name}" node_group = bpy.data.node_groups.new(type="ShaderNodeTree", name=group_name) # Create input and output sockets for the node group node_group.interface.new_socket( name="UV", in_out="INPUT", socket_type="NodeSocketVector" ) node_group.interface.new_socket( name="Vector", in_out="OUTPUT", socket_type="NodeSocketVector" ) # Add nodes to the group _group_input = node_group.nodes.new("NodeGroupInput") group_output = node_group.nodes.new("NodeGroupOutput") # Create a single UV Map node uv_map_node = node_group.nodes.new(type="ShaderNodeUVMap") uv_map_node.name = "VRM_TextureTransform_UVMap" # Setup drivers and create mapping nodes mapping_nodes = self.setup_drivers( context, armature, node_group, material, all_expressions ) # Link the UV Map node to all mapping nodes for mapping_node in mapping_nodes.values(): node_group.links.new(uv_map_node.outputs[0], mapping_node.inputs[0]) # Create blocking multiply chains multiply_chains = self.create_blocking_multiply_chains( node_group, all_expressions, armature, mapping_nodes ) # Create the final add chain final_add_node = self.create_final_add_chain( node_group, mapping_nodes, multiply_chains, all_expressions ) # Connect the final add node to the group output if final_add_node: node_group.links.new(final_add_node.outputs[0], group_output.inputs[0]) # Add the group node to the material group_node = node_tree.nodes.new(type="ShaderNodeGroup") if not isinstance(group_node, ShaderNodeGroup): raise TypeError group_node.node_tree = node_group group_node.name = "VRM_TextureTransform_Group" # Connect the group node to texture nodes for node in node_tree.nodes: if node.type == "TEX_IMAGE": if not isinstance(node, ShaderNodeTexImage): raise TypeError self.connect_group_to_image_node(node_tree, group_node, node) def setup_drivers( self, _context: Context, armature: Object, node_group: NodeTree, material: Material, all_expressions: Sequence[tuple[Vrm1ExpressionPropertyGroup, str, str]], ) -> Mapping[str, ShaderNodeMapping]: mapping_nodes = self.create_mapping_nodes( node_group, all_expressions, armature, material ) return mapping_nodes def create_mapping_nodes( self, node_group: NodeTree, all_expressions: Sequence[tuple[Vrm1ExpressionPropertyGroup, str, str]], armature: Object, material: Material, ) -> Mapping[str, ShaderNodeMapping]: mapping_nodes: dict[str, ShaderNodeMapping] = {} for expression, expr_type, expr_name in all_expressions: for bind_index, bind in enumerate(expression.texture_transform_binds): if bind.material == material: mapping_node = node_group.nodes.new(type="ShaderNodeMapping") if not isinstance(mapping_node, ShaderNodeMapping): raise TypeError mapping_node.name = ( f"VRM_TextureTransform_Mapping_{expr_type}_{expr_name}" ) scale_input_node = mapping_node.inputs["Scale"] if not isinstance(scale_input_node, shader.VECTOR_SOCKET_CLASSES): raise TypeError scale_input_node.default_value = (1, 1, 1) mapping_nodes[f"{expr_type}_{expr_name}"] = mapping_node self.setup_mapping_node_drivers( mapping_node, expression, expr_type, expr_name, bind_index, armature, ) return mapping_nodes def setup_mapping_node_drivers( self, mapping_node: ShaderNodeMapping, _expression: Vrm1ExpressionPropertyGroup, expr_type: str, expr_name: str, bind_index: int, armature: Object, ) -> None: base_path = f"vrm_addon_extension.vrm1.expressions.{expr_type}" custom_index = f"[{expr_name}]" if expr_type == "custom" else f".{expr_name}" # Set all scale values to 0.0 scale_input_socket = mapping_node.inputs["Scale"] if not isinstance(scale_input_socket, shader.VECTOR_SOCKET_CLASSES): raise TypeError scale_input_socket.default_value = (0.0, 0.0, 0.0) for i, axis in enumerate(["X", "Y"]): for input_name, property_name in [ ("Location", "offset"), ("Scale", "scale"), ]: fcurve = mapping_node.inputs[input_name].driver_add("default_value", i) if not isinstance(fcurve, FCurve): raise TypeError driver = fcurve.driver if not driver: raise TypeError driver.type = "SCRIPTED" var = driver.variables.new() var.name = f"{property_name}_{axis.lower()}" var.type = "SINGLE_PROP" var.targets[0].id_type = "ARMATURE" armature_data = armature.data if not isinstance(armature_data, ID): raise TypeError var.targets[0].id = armature_data data_path = ( f"{base_path}{custom_index}" f".texture_transform_binds[{bind_index}].{property_name}[{i}]" ) var.targets[0].data_path = data_path preview_var = driver.variables.new() preview_var.name = "preview" preview_var.type = "SINGLE_PROP" preview_var.targets[0].id_type = "ARMATURE" preview_var.targets[0].id = armature_data preview_var.targets[0].data_path = f"{base_path}{custom_index}.preview" is_binary_var = driver.variables.new() is_binary_var.name = "is_binary" is_binary_var.type = "SINGLE_PROP" is_binary_var.targets[0].id_type = "ARMATURE" is_binary_var.targets[0].id = armature_data is_binary_var.targets[ 0 ].data_path = f"{base_path}{custom_index}.is_binary" if input_name == "Location": if axis == "X": # Offset UV as expected driver.expression = ( f"{property_name}_{axis.lower()} * (1.0 if is_binary and " " preview > 0.5 else (0.0 if is_binary else preview))" ) else: # Offset UV in the opposite direction # (this is a quirk of VRM standard) driver.expression = ( f"(-{property_name}_{axis.lower()} " " + ( " " 0 if scale_y == 1 " " else (1 - scale_y) " " ) " " ) " " * (1.0 if is_binary and preview > 0.5 " " else (0.0 if is_binary else preview) " " ) " " - (0 if scale_y == 1 else 1)" ) elif axis == "X": driver.expression = ( f"({property_name}_{axis.lower()} - 1) * " " ( " " 1.0 if is_binary and preview > 0.5 " " else (0.0 if is_binary else preview) " " )" ) else: driver.expression = ( f"-(2 - {property_name}_{axis.lower()} - 1) " " * (1.0 if is_binary and preview > 0.5 " " else (0.0 if is_binary else preview) " " )" ) # Add scale_y variable for Y offset calculation if input_name == "Location" and axis == "Y": scale_y_var = driver.variables.new() scale_y_var.name = "scale_y" scale_y_var.type = "SINGLE_PROP" scale_y_var.targets[0].id_type = "ARMATURE" scale_y_var.targets[0].id = armature_data data_path = ( f"{base_path}{custom_index}" f".texture_transform_binds[{bind_index}].scale[1]" ) scale_y_var.targets[0].data_path = data_path def create_blocking_multiply_chains( self, node_group: NodeTree, all_expressions: Sequence[tuple[Vrm1ExpressionPropertyGroup, str, str]], armature: Object, mapping_nodes: Mapping[str, ShaderNodeMapping], ) -> Mapping[str, Optional[ShaderNodeVectorMath]]: multiply_chains: dict[str, Optional[ShaderNodeVectorMath]] = {} blockable_types = ["blink", "look", "mouth"] for blockable_type in blockable_types: chain = self.create_blocking_multiply_chain( node_group, blockable_type, all_expressions, armature, mapping_nodes ) multiply_chains[blockable_type] = chain return multiply_chains def create_blocking_multiply_chain( self, node_group: NodeTree, blockable_type: str, all_expressions: Sequence[tuple[Vrm1ExpressionPropertyGroup, str, str]], armature: Object, mapping_nodes: Mapping[str, ShaderNodeMapping], ) -> Optional[ShaderNodeVectorMath]: blockable_expressions = self.get_blockable_expressions(blockable_type) # Add blockable expressions together blockable_add_node = self.add_blockable_expressions( node_group, blockable_expressions, mapping_nodes ) # Create blocking value nodes value_nodes = self.create_blocking_value_nodes( node_group, blockable_type, all_expressions, armature ) # Create and link multiply nodes last_multiply = None for i, value_node in enumerate(value_nodes): multiply_node = node_group.nodes.new(type="ShaderNodeVectorMath") if not isinstance(multiply_node, ShaderNodeVectorMath): raise TypeError multiply_node.operation = "MULTIPLY" multiply_node.name = ( f"VRM_TextureTransform_BlockingMultiply_{blockable_type}_{i}" ) if last_multiply: node_group.links.new(last_multiply.outputs[0], multiply_node.inputs[0]) elif blockable_add_node: # Connect the blockable add node to the first multiply node node_group.links.new( blockable_add_node.outputs[0], multiply_node.inputs[0] ) node_group.links.new(value_node.outputs[0], multiply_node.inputs[1]) last_multiply = multiply_node return ( last_multiply or blockable_add_node ) # Return blockable_add_node if no multiply nodes were created def add_blockable_expressions( self, node_group: NodeTree, blockable_expressions: Sequence[str], mapping_nodes: Mapping[str, ShaderNodeMapping], ) -> Optional[ShaderNodeVectorMath]: add_node = None for _i, expr_name in enumerate(blockable_expressions): mapping_node = mapping_nodes.get(f"preset_{expr_name}") if mapping_node: if add_node is None: node = node_group.nodes.new(type="ShaderNodeVectorMath") if not isinstance(node, ShaderNodeVectorMath): raise TypeError add_node = node add_node.operation = "ADD" add_node.name = f"VRM_TextureTransform_BlockableAdd_{expr_name}" node_group.links.new(mapping_node.outputs[0], add_node.inputs[0]) else: new_add_node = node_group.nodes.new(type="ShaderNodeVectorMath") if not isinstance(new_add_node, ShaderNodeVectorMath): raise TypeError new_add_node.operation = "ADD" new_add_node.name = f"VRM_TextureTransform_BlockableAdd_{expr_name}" node_group.links.new(add_node.outputs[0], new_add_node.inputs[0]) node_group.links.new( mapping_node.outputs[0], new_add_node.inputs[1] ) add_node = new_add_node return add_node def create_blocking_value_nodes( self, node_group: NodeTree, blockable_type: str, all_expressions: Sequence[tuple[object, str, str]], armature: Object, ) -> Sequence[ShaderNodeValue]: value_nodes: list[ShaderNodeValue] = [] for _expr, expr_type, expr_name in all_expressions: if ( expr_type == "preset" and expr_name not in self.get_blockable_expressions(blockable_type) ): armature_data = armature.data if not isinstance(armature_data, Armature): raise TypeError override_blockable_type = getattr( getattr( get_armature_extension(armature_data).vrm1.expressions.preset, expr_name, None, ), "override_{blockable_type}", None, ) include_expression = override_blockable_type != "none" if include_expression: value_node = node_group.nodes.new(type="ShaderNodeValue") if not isinstance(value_node, ShaderNodeValue): raise TypeError value_node.name = ( "VRM_TextureTransform_BlockingValue_" f"{blockable_type}_{expr_name}" ) value_nodes.append(value_node) fcurve = value_node.outputs[0].driver_add("default_value") if not isinstance(fcurve, FCurve): raise TypeError driver = fcurve.driver if not driver: raise TypeError driver.type = "SCRIPTED" preview_var = driver.variables.new() preview_var.name = "preview" preview_var.type = "SINGLE_PROP" preview_var.targets[0].id_type = "ARMATURE" armature_data = armature.data if not isinstance(armature_data, ID): raise TypeError preview_var.targets[0].id = armature_data data_path = ( "vrm_addon_extension.vrm1.expressions.preset" f".{expr_name}.preview" ) preview_var.targets[0].data_path = data_path driver.expression = "0 if preview >= 0.5 else 1" return value_nodes def create_add_node( self, node_group: NodeTree, input_node: Node, previous_add_node: Optional[ShaderNodeVectorMath], ) -> ShaderNodeVectorMath: add_node = node_group.nodes.new(type="ShaderNodeVectorMath") if not isinstance(add_node, ShaderNodeVectorMath): raise TypeError add_node.operation = "ADD" add_node.name = f"VRM_TextureTransform_Add_{input_node.name}" node_group.links.new(input_node.outputs[0], add_node.inputs[0]) if previous_add_node: node_group.links.new(previous_add_node.outputs[0], add_node.inputs[1]) return add_node def create_final_add_chain( self, node_group: NodeTree, mapping_nodes: Mapping[str, Node], multiply_chains: Mapping[str, Optional[ShaderNodeVectorMath]], all_expressions: Sequence[tuple[object, str, str]], ) -> Optional[ShaderNodeVectorMath]: add_nodes: list[ShaderNodeVectorMath] = [] last_add = None # Add non-blockable mapping nodes for _expr, expr_type, expr_name in all_expressions: if not self.is_blockable_expression(expr_name): mapping_node = mapping_nodes.get(f"{expr_type}_{expr_name}") if mapping_node: add_node = self.create_add_node(node_group, mapping_node, last_add) last_add = add_node add_nodes.append(add_node) # Add results from multiply chains for last_multiply in multiply_chains.values(): if last_multiply: add_node = self.create_add_node(node_group, last_multiply, last_add) last_add = add_node add_nodes.append(add_node) return last_add def is_blockable_expression(self, expr_name: str) -> bool: blockable_types = ["blink", "look", "mouth"] return any( expr_name in self.get_blockable_expressions(bt) for bt in blockable_types ) def get_property_value(self, armature: Object, data_path: str) -> object: prop: object = armature for prop_name in data_path.split("."): if not re.match(prop_name, "^[a-zA-Z_][a-zA-Z0-9_]*$"): message = f'Invalid prop name: "{prop_name}"' raise AssertionError(message) prop = getattr(prop, prop_name) return prop def get_blockable_expressions(self, blockable_type: str) -> Sequence[str]: if blockable_type == "blink": return ["blink", "blink_left", "blink_right"] if blockable_type == "look": return ["look_up", "look_down", "look_left", "look_right"] if blockable_type == "mouth": return ["aa", "ih", "ee", "oh", "ou"] return [] def remove_existing_nodes(self, node_tree: ShaderNodeTree) -> None: nodes_to_remove: list[Node] = [] links_to_restore: list[tuple[NodeSocket, NodeSocket]] = [] for node in node_tree.nodes: if node.name.startswith("VRM_TextureTransform_"): nodes_to_remove.append(node) # Store information about links to restore for output in node.outputs: for link in output.links: if not link.to_node.name.startswith("VRM_TextureTransform_"): # Find the first input that doesn't start # with VRM_TextureTransform_ for input_socket in node.inputs: if input_socket.is_linked and not input_socket.links[ 0 ].from_node.name.startswith("VRM_TextureTransform_"): links_to_restore.append( ( input_socket.links[0].from_socket, link.to_socket, ) ) break elif ( node.type == "GROUP" and isinstance(node, ShaderNodeGroup) and (node_node_tree := node.node_tree) and node_node_tree.name.startswith("VRM_TextureTransform_") ): nodes_to_remove.append(node) # Handle links for group nodes for output in node.outputs: links_to_restore.extend( (node.inputs[0].links[0].from_socket, link.to_socket) for link in output.links if not link.to_node.name.startswith("VRM_TextureTransform_") ) # Remove nodes for node in nodes_to_remove: node_tree.nodes.remove(node) # Restore links for from_socket, to_socket in links_to_restore: node_tree.links.new(from_socket, to_socket) # Remove orphaned node groups for group in bpy.data.node_groups: if group.name.startswith("VRM_TextureTransform_") and not group.users: bpy.data.node_groups.remove(group) def connect_group_to_image_node( self, node_tree: ShaderNodeTree, group_node: ShaderNodeGroup, image_node: ShaderNodeTexImage, ) -> None: vector_input = image_node.inputs["Vector"] existing_links = vector_input.links if not existing_links: node_tree.links.new(group_node.outputs[0], vector_input) else: vector_math_node = node_tree.nodes.new(type="ShaderNodeVectorMath") if not isinstance(vector_math_node, ShaderNodeVectorMath): raise TypeError vector_math_node.name = f"VRM_TextureTransform_VectorMath_{image_node.name}" vector_math_node.operation = "ADD" node_tree.links.new(group_node.outputs[0], vector_math_node.inputs[0]) node_tree.links.new( existing_links[0].from_socket, vector_math_node.inputs[1] ) node_tree.links.remove(existing_links[0]) node_tree.links.new(vector_math_node.outputs[0], vector_input) if TYPE_CHECKING: # This code is auto generated. # To regenerate, run the `uv run tools/property_typing.py` command. armature_object_name: str # type: ignore[no-redef] armature_name: str # type: ignore[no-redef] expression_name: str # type: ignore[no-redef] def draw_bone_prop_search( layout: UILayout, human_bone_specification: HumanBoneSpecification, armature: Object, *, show_diagnostics: bool = True, ) -> None: armature_data = armature.data if not isinstance(armature_data, Armature): return ext = get_armature_extension(armature_data) human_bone = ext.vrm1.humanoid.human_bones.human_bone_name_to_human_bone().get( human_bone_specification.name ) if not human_bone: return row = layout.row(align=True) row.prop_search( human_bone.node, "bone_name", human_bone, "node_candidates", text="", translate=False, icon=human_bone_specification.icon, ) if not show_diagnostics: return if ( human_bone.node.bone_name and human_bone.node.bone_name not in human_bone.node_candidates ): icon = "ERROR" row.alert = True else: icon = "VIEWZOOM" op = layout_operator( row, VRM_OT_show_vrm1_bone_assignment_diagnostics, text="", translate=False, icon=icon, ) op.armature_object_name = armature.name op.human_bone_name = human_bone_specification.name.value row.separator(factor=0.5) class VRM_OT_show_vrm1_bone_assignment_diagnostics(Operator): bl_idname = "vrm.show_vrm1_bone_assignment_diagnostics" bl_label = "Bone Assignment Diagnostics" bl_description = ( "Shows the cause of the bone assignment error" " or the reason why none of the bone assignment candidates exist." ) def execute(self, _context: Context) -> set[str]: return {"FINISHED"} armature_object_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"} ) human_bone_name: StringProperty( # type: ignore[valid-type] options={"HIDDEN"} ) def invoke(self, context: Context, _event: Event) -> set[str]: obj = context.blend_data.objects.get(self.armature_object_name) if not obj or obj.type != "ARMATURE": return {"CANCELLED"} if not HumanBoneName.from_str(self.human_bone_name): return {"CANCELLED"} return context.window_manager.invoke_props_dialog(self, width=800) def draw(self, context: Context) -> None: armature = context.blend_data.objects.get(self.armature_object_name) if not armature: return armature_data = armature.data if not isinstance(armature_data, Armature): return human_bone_name = HumanBoneName.from_str(self.human_bone_name) if not human_bone_name: return human_bone_specification = HumanBoneSpecifications.get(human_bone_name) layout = self.layout head_row = layout.row() head_row.alignment = "LEFT" head_row.label( text=pgettext('Assignment of the VRM Human Bone "{vrm_human_bone}"').format( vrm_human_bone=human_bone_specification.title ), icon="ARMATURE_DATA", ) draw_bone_prop_search( head_row, human_bone_specification, armature, show_diagnostics=False ) help_op = layout_operator( head_row, VRM_OT_open_url_in_web_browser, icon="URL", text="Open help in a Web Browser", ) help_op.url = pgettext( "https://github.com/vrm-c/vrm-specification/blob/c24d76d99a18738dd2c266be1c83f089064a7b5e/specification/VRMC_vrm-1.0/humanoid.md#humanoid-bone-parent-child-relationship" ) ext = get_armature_extension(armature_data) humanoid = ext.vrm1.humanoid human_bone_name_to_human_bone = ( humanoid.human_bones.human_bone_name_to_human_bone() ) human_bone = human_bone_name_to_human_bone.get(human_bone_name) if not human_bone: return bone_name = human_bone.node.bone_name if bone_name in human_bone.node_candidates: layout.label( text=pgettext( 'The bone "{bone_name}" of the armature "{armature_object_name}"' + ' can be assigned to the VRM Human Bone "{human_bone_name}".' ).format( armature_object_name=self.armature_object_name, bone_name=bone_name, human_bone_name=human_bone_specification.title, ), translate=False, icon="CHECKMARK", ) elif bone_name: layout.label( text=pgettext( 'The bone "{bone_name}" of the armature "{armature_object_name}"' + ' cannot be assigned to the VRM Human Bone "{human_bone_name}".' ).format( armature_object_name=self.armature_object_name, bone_name=bone_name, human_bone_name=human_bone_specification.title, ), translate=False, icon="ERROR", ) elif not human_bone.node_candidates: layout.label( text=pgettext( 'The armature "{armature_object_name}" does not have any bones' + ' that can be assigned to the VRM Human Bone "{human_bone_name}".' ).format( armature_object_name=self.armature_object_name, human_bone_name=human_bone_specification.title, ), translate=False, icon="ERROR", ) bpy_bone_name_to_human_bone_specification = { bpy_bone_name: HumanBoneSpecifications.get(human_bone_name) for human_bone_name, human_bone in human_bone_name_to_human_bone.items() if (bpy_bone_name := human_bone.node.bone_name) } error_bpy_bone_names = [ bpy_bone_name for human_bone in human_bone_name_to_human_bone.values() if (bpy_bone_name := human_bone.node.bone_name) and bpy_bone_name not in human_bone.node_candidates ] Vrm1HumanBonesPropertyGroup.update_all_node_candidates( context, armature_data.name ) BonePropertyGroup.find_bone_candidates( armature_data, human_bone_specification, bpy_bone_name_to_human_bone_specification, error_bpy_bone_names, layout, ) if TYPE_CHECKING: # This code is auto generated. # To regenerate, run the `uv run tools/property_typing.py` command. armature_object_name: str # type: ignore[no-redef] human_bone_name: str # type: ignore[no-redef]