- Add core VRM addon infrastructure with manifest and registration - Add common utilities module with file system, logging, and conversion helpers - Add human bone mapper with support for multiple rigging standards (Mixamo, MMD, Unreal, Rigify, etc.) - Add VRM 0.x and 1.x format support with property groups and handlers - Add editor UI panels for VRM metadata, spring bones, and MToon materials - Add exporter with glTF2 extension support for VRM format serialization - Add importer with scene reconstruction and armature generation - Add MToon shader support with auto-setup and material migration - Add spring bone physics simulation with constraint handling - Add node constraint editor for advanced rigging control - Add comprehensive validation and error handling with user dialogs - Add scene watcher for real-time property synchronization - Add workspace management and preference system - Include Python cache files and Blender manifest configuration - This is the initial commit establishing the complete VRM addon ecosystem for Blender
2731 lines
109 KiB
Python
2731 lines
109 KiB
Python
# 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]
|