@volter/blender-engine 0.1.148 → 0.1.149
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +16 -0
- package/README.md +4 -11
- package/browser/addons/vrm/LICENSE_(OPTION1)_MIT.txt +22 -0
- package/browser/addons/vrm/LICENSE_(OPTION2)_GPL-3_0.txt +674 -0
- package/browser/addons/vrm/LICENSE_MAIN.txt +1 -0
- package/browser/addons/vrm/README.txt +3 -0
- package/browser/addons/vrm/__init__.py +41 -0
- package/browser/addons/vrm/blender_manifest.toml +16 -0
- package/browser/addons/vrm/common/__init__.py +1 -0
- package/browser/addons/vrm/common/animation.py +58 -0
- package/browser/addons/vrm/common/blender_manifest.py +82 -0
- package/browser/addons/vrm/common/char.py +16 -0
- package/browser/addons/vrm/common/convert.py +287 -0
- package/browser/addons/vrm/common/convert_any.py +48 -0
- package/browser/addons/vrm/common/debug.py +40 -0
- package/browser/addons/vrm/common/deep.py +162 -0
- package/browser/addons/vrm/common/error_dialog.py +274 -0
- package/browser/addons/vrm/common/fs.py +71 -0
- package/browser/addons/vrm/common/gl.py +20 -0
- package/browser/addons/vrm/common/gltf.py +1149 -0
- package/browser/addons/vrm/common/human_bone_mapper/__init__.py +1 -0
- package/browser/addons/vrm/common/human_bone_mapper/biped_mapping.py +84 -0
- package/browser/addons/vrm/common/human_bone_mapper/cats_blender_plugin_fix_model_mapping.py +63 -0
- package/browser/addons/vrm/common/human_bone_mapper/human_bone_mapper.py +203 -0
- package/browser/addons/vrm/common/human_bone_mapper/microsoft_rocketbox_mapping.py +69 -0
- package/browser/addons/vrm/common/human_bone_mapper/mixamo_mapping.py +62 -0
- package/browser/addons/vrm/common/human_bone_mapper/mmd_mapping.py +115 -0
- package/browser/addons/vrm/common/human_bone_mapper/mpfb_mapping.py +120 -0
- package/browser/addons/vrm/common/human_bone_mapper/ready_player_me_mapping.py +65 -0
- package/browser/addons/vrm/common/human_bone_mapper/reallusion_mapping.py +94 -0
- package/browser/addons/vrm/common/human_bone_mapper/rigify_meta_rig_mapping.py +65 -0
- package/browser/addons/vrm/common/human_bone_mapper/structure_based_mapping.py +891 -0
- package/browser/addons/vrm/common/human_bone_mapper/unreal_mapping.py +62 -0
- package/browser/addons/vrm/common/human_bone_mapper/vrm_addon_mapping.py +112 -0
- package/browser/addons/vrm/common/human_bone_mapper/vroid_mapping.py +89 -0
- package/browser/addons/vrm/common/legacy_gltf.py +16 -0
- package/browser/addons/vrm/common/logger.py +84 -0
- package/browser/addons/vrm/common/mtoon0.blend +0 -0
- package/browser/addons/vrm/common/mtoon1.blend +0 -0
- package/browser/addons/vrm/common/mtoon1_outline.blend +0 -0
- package/browser/addons/vrm/common/mtoon_auto_setup.blend +0 -0
- package/browser/addons/vrm/common/mtoon_unversioned.py +62 -0
- package/browser/addons/vrm/common/ops/__init__.py +10 -0
- package/browser/addons/vrm/common/ops/export_scene.py +67 -0
- package/browser/addons/vrm/common/ops/icyp.py +53 -0
- package/browser/addons/vrm/common/ops/import_scene.py +57 -0
- package/browser/addons/vrm/common/ops/vrm.py +2380 -0
- package/browser/addons/vrm/common/ops/wm.py +167 -0
- package/browser/addons/vrm/common/preferences.py +362 -0
- package/browser/addons/vrm/common/progress.py +87 -0
- package/browser/addons/vrm/common/rotation.py +89 -0
- package/browser/addons/vrm/common/safe_removal.py +46 -0
- package/browser/addons/vrm/common/scene_watcher.py +230 -0
- package/browser/addons/vrm/common/shader.py +2243 -0
- package/browser/addons/vrm/common/shape_key_mapper/__init__.py +1 -0
- package/browser/addons/vrm/common/shape_key_mapper/arkit_mapping.py +168 -0
- package/browser/addons/vrm/common/shape_key_mapper/mmd_mapping.py +62 -0
- package/browser/addons/vrm/common/shape_key_mapper/ready_player_me_mapping.py +52 -0
- package/browser/addons/vrm/common/shape_key_mapper/vrchat_mapping.py +17 -0
- package/browser/addons/vrm/common/third_party_user_extension.py +214 -0
- package/browser/addons/vrm/common/transparent_z_write.blend +0 -0
- package/browser/addons/vrm/common/version.py +161 -0
- package/browser/addons/vrm/common/vrm0/__init__.py +1 -0
- package/browser/addons/vrm/common/vrm0/human_bone.py +662 -0
- package/browser/addons/vrm/common/vrm0/material_property.py +346 -0
- package/browser/addons/vrm/common/vrm1/__init__.py +1 -0
- package/browser/addons/vrm/common/vrm1/expression_preset.py +207 -0
- package/browser/addons/vrm/common/vrm1/human_bone.py +822 -0
- package/browser/addons/vrm/common/workspace.py +227 -0
- package/browser/addons/vrm/common/writable_context.py +77 -0
- package/browser/addons/vrm/documentation/en-us/animation/index.md +63 -0
- package/browser/addons/vrm/documentation/en-us/create-humanoid-vrm-from-scratch/index.md +131 -0
- package/browser/addons/vrm/documentation/en-us/create-simple-vrm-from-scratch/index.md +30 -0
- package/browser/addons/vrm/documentation/en-us/development/index.md +136 -0
- package/browser/addons/vrm/documentation/en-us/index.md +41 -0
- package/browser/addons/vrm/documentation/en-us/installation/index.md +152 -0
- package/browser/addons/vrm/documentation/en-us/material-mtoon/index.md +122 -0
- package/browser/addons/vrm/documentation/en-us/material-pbr/index.md +94 -0
- package/browser/addons/vrm/documentation/en-us/scripting-api/index.md +391 -0
- package/browser/addons/vrm/documentation/en-us/ui/export_scene.vrm/index.md +57 -0
- package/browser/addons/vrm/documentation/en-us/ui/export_scene.vrma/index.md +17 -0
- package/browser/addons/vrm/documentation/en-us/ui/import_scene.vrm/index.md +61 -0
- package/browser/addons/vrm/documentation/en-us/ui/import_scene.vrma/index.md +13 -0
- package/browser/addons/vrm/documentation/ja-jp/animation/index.md +57 -0
- package/browser/addons/vrm/documentation/ja-jp/create-humanoid-vrm-from-scratch/index.md +129 -0
- package/browser/addons/vrm/documentation/ja-jp/create-simple-vrm-from-scratch/index.md +29 -0
- package/browser/addons/vrm/documentation/ja-jp/development/index.md +137 -0
- package/browser/addons/vrm/documentation/ja-jp/index.md +37 -0
- package/browser/addons/vrm/documentation/ja-jp/installation/index.md +150 -0
- package/browser/addons/vrm/documentation/ja-jp/material-mtoon/index.md +113 -0
- package/browser/addons/vrm/documentation/ja-jp/material-pbr/index.md +90 -0
- package/browser/addons/vrm/documentation/ja-jp/scripting-api/index.md +389 -0
- package/browser/addons/vrm/documentation/ja-jp/ui/export_scene.vrm/index.md +55 -0
- package/browser/addons/vrm/documentation/ja-jp/ui/export_scene.vrma/index.md +19 -0
- package/browser/addons/vrm/documentation/ja-jp/ui/import_scene.vrm/index.md +56 -0
- package/browser/addons/vrm/documentation/ja-jp/ui/import_scene.vrma/index.md +14 -0
- package/browser/addons/vrm/editor/__init__.py +1 -0
- package/browser/addons/vrm/editor/extension.py +425 -0
- package/browser/addons/vrm/editor/extension_accessor.py +75 -0
- package/browser/addons/vrm/editor/handler.py +84 -0
- package/browser/addons/vrm/editor/khr_character/__init__.py +1 -0
- package/browser/addons/vrm/editor/khr_character/panel.py +69 -0
- package/browser/addons/vrm/editor/khr_character/property_group.py +27 -0
- package/browser/addons/vrm/editor/khr_xmp_json_ld/__init__.py +1 -0
- package/browser/addons/vrm/editor/khr_xmp_json_ld/ops.py +464 -0
- package/browser/addons/vrm/editor/khr_xmp_json_ld/panel.py +136 -0
- package/browser/addons/vrm/editor/khr_xmp_json_ld/property_group.py +83 -0
- package/browser/addons/vrm/editor/khr_xmp_json_ld/ui_list.py +198 -0
- package/browser/addons/vrm/editor/make_armature.py +709 -0
- package/browser/addons/vrm/editor/menu.py +147 -0
- package/browser/addons/vrm/editor/migration.py +297 -0
- package/browser/addons/vrm/editor/mtoon1/__init__.py +1 -0
- package/browser/addons/vrm/editor/mtoon1/handler.py +20 -0
- package/browser/addons/vrm/editor/mtoon1/migration.py +536 -0
- package/browser/addons/vrm/editor/mtoon1/ops.py +304 -0
- package/browser/addons/vrm/editor/mtoon1/panel.py +442 -0
- package/browser/addons/vrm/editor/mtoon1/property_group.py +4977 -0
- package/browser/addons/vrm/editor/mtoon1/scene_watcher.py +381 -0
- package/browser/addons/vrm/editor/node_constraint1/__init__.py +1 -0
- package/browser/addons/vrm/editor/node_constraint1/panel.py +317 -0
- package/browser/addons/vrm/editor/node_constraint1/property_group.py +25 -0
- package/browser/addons/vrm/editor/ops.py +471 -0
- package/browser/addons/vrm/editor/panel.py +315 -0
- package/browser/addons/vrm/editor/property_group.py +888 -0
- package/browser/addons/vrm/editor/search.py +682 -0
- package/browser/addons/vrm/editor/spring_bone1/__init__.py +1 -0
- package/browser/addons/vrm/editor/spring_bone1/handler.py +994 -0
- package/browser/addons/vrm/editor/spring_bone1/menu.py +246 -0
- package/browser/addons/vrm/editor/spring_bone1/migration.py +86 -0
- package/browser/addons/vrm/editor/spring_bone1/ops.py +1679 -0
- package/browser/addons/vrm/editor/spring_bone1/panel.py +605 -0
- package/browser/addons/vrm/editor/spring_bone1/property_group.py +1457 -0
- package/browser/addons/vrm/editor/spring_bone1/ui_list.py +408 -0
- package/browser/addons/vrm/editor/subscription.py +123 -0
- package/browser/addons/vrm/editor/t_pose.py +713 -0
- package/browser/addons/vrm/editor/validation.py +1271 -0
- package/browser/addons/vrm/editor/vrm0/__init__.py +1 -0
- package/browser/addons/vrm/editor/vrm0/gizmo_group.py +63 -0
- package/browser/addons/vrm/editor/vrm0/handler.py +23 -0
- package/browser/addons/vrm/editor/vrm0/menu.py +145 -0
- package/browser/addons/vrm/editor/vrm0/migration.py +761 -0
- package/browser/addons/vrm/editor/vrm0/ops.py +2145 -0
- package/browser/addons/vrm/editor/vrm0/panel.py +1333 -0
- package/browser/addons/vrm/editor/vrm0/property_group.py +1818 -0
- package/browser/addons/vrm/editor/vrm0/ui_list.py +688 -0
- package/browser/addons/vrm/editor/vrm1/__init__.py +1 -0
- package/browser/addons/vrm/editor/vrm1/handler.py +28 -0
- package/browser/addons/vrm/editor/vrm1/menu.py +76 -0
- package/browser/addons/vrm/editor/vrm1/migration.py +321 -0
- package/browser/addons/vrm/editor/vrm1/ops.py +2679 -0
- package/browser/addons/vrm/editor/vrm1/panel.py +1243 -0
- package/browser/addons/vrm/editor/vrm1/property_group.py +2087 -0
- package/browser/addons/vrm/editor/vrm1/scene_watcher.py +90 -0
- package/browser/addons/vrm/editor/vrm1/ui_list.py +533 -0
- package/browser/addons/vrm/exporter/__init__.py +1 -0
- package/browser/addons/vrm/exporter/abstract_base_vrm_exporter.py +628 -0
- package/browser/addons/vrm/exporter/export_scene.py +930 -0
- package/browser/addons/vrm/exporter/gltf2_export_user_extension.py +45 -0
- package/browser/addons/vrm/exporter/khr_character_exporter.py +34 -0
- package/browser/addons/vrm/exporter/vrm0_exporter.py +4020 -0
- package/browser/addons/vrm/exporter/vrm1_exporter.py +3682 -0
- package/browser/addons/vrm/exporter/vrm_animation_exporter.py +1035 -0
- package/browser/addons/vrm/external/__init__.py +1 -0
- package/browser/addons/vrm/external/auto_rig_pro_support.py +3 -0
- package/browser/addons/vrm/external/io_scene_gltf2_support.py +418 -0
- package/browser/addons/vrm/external/rigify_support.py +1 -0
- package/browser/addons/vrm/importer/__init__.py +1 -0
- package/browser/addons/vrm/importer/abstract_base_vrm_importer.py +1548 -0
- package/browser/addons/vrm/importer/file_handler.py +34 -0
- package/browser/addons/vrm/importer/gltf2_import_user_extension.py +268 -0
- package/browser/addons/vrm/importer/import_scene.py +646 -0
- package/browser/addons/vrm/importer/license_validation.py +198 -0
- package/browser/addons/vrm/importer/vrm0_importer.py +1723 -0
- package/browser/addons/vrm/importer/vrm1_importer.py +2014 -0
- package/browser/addons/vrm/importer/vrm_animation_importer.py +763 -0
- package/browser/addons/vrm/importer/vrm_diff.py +316 -0
- package/browser/addons/vrm/locale/__init__.py +1 -0
- package/browser/addons/vrm/locale/ja_jp.py +826 -0
- package/browser/addons/vrm/locale/ruff.toml +8 -0
- package/browser/addons/vrm/locale/translation_dictionary.py +10 -0
- package/browser/addons/vrm/locale/zh_hans.py +928 -0
- package/browser/addons/vrm/registration.py +670 -0
- package/browser/addons/vrm.json +18 -0
- package/browser/blender-emscripten-engine.mts +60 -128
- package/browser/blender-engine.mts +20 -186
- package/browser/blender-wali-engine.mts +35 -81
- package/browser/describe-thrown.mts +3 -6
- package/browser/document-chunks.mts +4 -16
- package/browser/gltf_export.py +3161 -0
- package/browser/heap-growth.mts +23 -0
- package/browser/index.ts +2 -0
- package/browser/protocol.ts +119 -35
- package/browser/pull-job.mts +2 -10
- package/browser/rna.ts +190 -73
- package/browser/runtime.ts +197 -82
- package/browser/session-addons.mts +33 -0
- package/browser/session-frame.mts +5 -24
- package/browser/session.py +1338 -1427
- package/browser/three/attach-presenter.ts +11 -39
- package/browser/three/blender-base64.ts +1 -36
- package/browser/three/blender-corner-normals.ts +1 -23
- package/browser/three/blender-display-shaders.json +1 -1
- package/browser/three/blender-display-shaders.lut +0 -0
- package/browser/three/blender-display-transform.ts +2 -1
- package/browser/three/blender-frame-columns.ts +5 -13
- package/browser/three/blender-graph-material.ts +18 -3
- package/browser/three/blender-node-graph.ts +10 -34
- package/browser/three/blender-physical-material.ts +2 -31
- package/browser/three/blender-runtime-armature.ts +7 -60
- package/browser/three/blender-runtime-frame.ts +9 -23
- package/browser/three/blender-runtime-geometry.ts +22 -26
- package/browser/three/blender-runtime-lighting.ts +48 -178
- package/browser/three/blender-runtime-shadows.ts +187 -1
- package/browser/three/blender-runtime-skeleton.ts +53 -1
- package/browser/three/blender-runtime-view.ts +505 -283
- package/browser/three/blender-runtime-weights.ts +9 -72
- package/browser/three/blender-sky.ts +13 -33
- package/browser/three/blender-triangulate.ts +7 -35
- package/browser/three/blender-viewport-sampling.ts +235 -0
- package/browser/three/blender-workbench-material.ts +25 -29
- package/browser/three/blender-workbench-outline.ts +199 -0
- package/browser/three/blender-world-volume.ts +62 -10
- package/browser/three/presenter.ts +10 -26
- package/browser/three/release.ts +5 -21
- package/browser/three/sky-precompute-worker.ts +1 -18
- package/browser/three/sky-worker.ts +5 -23
- package/browser/three/world-field-sampler.ts +12 -26
- package/browser/volter_three.py +9 -105
- package/browser/worker.ts +156 -151
- package/package.json +5 -2
- package/wasm/BUNDLE.json +17 -17
- package/wasm/DEPENDENCY-LICENSES.txt +14 -0
- package/wasm/blender_browser.data.br +0 -0
- package/wasm/blender_browser.js +1 -1
- package/wasm/blender_browser.wasm.br +0 -0
- package/browser/frame-transfer.mts +0 -19
- package/browser/three/agx-base-srgb.lut +0 -0
- package/browser/three/agx-look-medium-high-contrast.lut +0 -0
- package/browser/three/agx-look-punchy.lut +0 -0
- package/browser/three/blender-agx.ts +0 -235
- package/browser/three/blender-display-lut.ts +0 -145
- package/browser/three/blender-filmic.ts +0 -49
- package/browser/three/blender-standard.ts +0 -68
- package/browser/three/blender-world-visibility.ts +0 -278
- package/browser/three/filmic-srgb.lut +0 -0
|
@@ -0,0 +1,2679 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT OR GPL-3.0-or-later
|
|
2
|
+
from collections.abc import Mapping, Sequence
|
|
3
|
+
from typing import TYPE_CHECKING, ClassVar, Optional, Protocol
|
|
4
|
+
|
|
5
|
+
import bpy
|
|
6
|
+
from bpy.app.translations import pgettext
|
|
7
|
+
from bpy.props import IntProperty, StringProperty
|
|
8
|
+
from bpy.types import (
|
|
9
|
+
ID,
|
|
10
|
+
Armature,
|
|
11
|
+
Context,
|
|
12
|
+
Event,
|
|
13
|
+
FCurve,
|
|
14
|
+
Material,
|
|
15
|
+
Mesh,
|
|
16
|
+
Node,
|
|
17
|
+
NodeSocket,
|
|
18
|
+
NodeTree,
|
|
19
|
+
Object,
|
|
20
|
+
Operator,
|
|
21
|
+
ShaderNodeGroup,
|
|
22
|
+
ShaderNodeMapping,
|
|
23
|
+
ShaderNodeTexImage,
|
|
24
|
+
ShaderNodeTree,
|
|
25
|
+
ShaderNodeValue,
|
|
26
|
+
ShaderNodeVectorMath,
|
|
27
|
+
UILayout,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
from ...common import shader
|
|
31
|
+
from ...common.human_bone_mapper.human_bone_mapper import create_human_bone_mapping
|
|
32
|
+
from ...common.logger import get_logger
|
|
33
|
+
from ...common.shader import LegacyAddonMaterial
|
|
34
|
+
from ...common.shape_key_mapper.arkit_mapping import (
|
|
35
|
+
ARKIT_SHAPE_KEYS,
|
|
36
|
+
VRM1_PRESET_TO_ARKIT_SHAPE_KEY_MAPPING,
|
|
37
|
+
)
|
|
38
|
+
from ...common.shape_key_mapper.mmd_mapping import VRM1_PRESET_TO_MMD_SHAPE_KEY_MAPPING
|
|
39
|
+
from ...common.shape_key_mapper.ready_player_me_mapping import (
|
|
40
|
+
VRM1_PRESET_TO_READY_PLAYER_ME_SHAPE_KEY_MAPPING,
|
|
41
|
+
)
|
|
42
|
+
from ...common.shape_key_mapper.vrchat_mapping import (
|
|
43
|
+
VRM1_PRESET_TO_VRCHAT_SHAPE_KEY_MAPPING,
|
|
44
|
+
)
|
|
45
|
+
from ...common.vrm0.human_bone import HumanBoneName as Vrm0HumanBoneName
|
|
46
|
+
from ...common.vrm0.material_property import (
|
|
47
|
+
GLTF_PROPERTIES,
|
|
48
|
+
MTOON0_PROPERTIES,
|
|
49
|
+
MaterialPropertyTarget,
|
|
50
|
+
MaterialPropertyType,
|
|
51
|
+
)
|
|
52
|
+
from ...common.vrm1.expression_preset import ExpressionPreset, ExpressionPresets
|
|
53
|
+
from ...common.vrm1.human_bone import (
|
|
54
|
+
HumanBoneName,
|
|
55
|
+
HumanBoneSpecification,
|
|
56
|
+
HumanBoneSpecifications,
|
|
57
|
+
)
|
|
58
|
+
from .. import search
|
|
59
|
+
from ..extension_accessor import get_armature_extension, get_material_extension
|
|
60
|
+
from ..menu import VRM_MT_bone_assignment
|
|
61
|
+
from ..ops import VRM_OT_open_url_in_web_browser, layout_operator
|
|
62
|
+
from ..property_group import HumanoidStructureBonePropertyGroup
|
|
63
|
+
from ..vrm0.property_group import (
|
|
64
|
+
Vrm0HumanoidPropertyGroup,
|
|
65
|
+
)
|
|
66
|
+
from .property_group import (
|
|
67
|
+
Vrm1ExpressionPropertyGroup,
|
|
68
|
+
Vrm1HumanBonesPropertyGroup,
|
|
69
|
+
Vrm1MaterialColorBindPropertyGroup,
|
|
70
|
+
reset_expression_material_binds,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
logger = get_logger(__name__)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class VRM_OT_add_vrm1_meta_author(Operator):
|
|
77
|
+
bl_idname = "vrm.add_vrm1_meta_author"
|
|
78
|
+
bl_label = "Add Author"
|
|
79
|
+
bl_description = "Add VRM 1.0 Meta Author"
|
|
80
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
81
|
+
|
|
82
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
83
|
+
options={"HIDDEN"},
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
def execute(self, context: Context) -> set[str]:
|
|
87
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
88
|
+
if armature is None or armature.type != "ARMATURE":
|
|
89
|
+
return {"CANCELLED"}
|
|
90
|
+
armature_data = armature.data
|
|
91
|
+
if not isinstance(armature_data, Armature):
|
|
92
|
+
return {"CANCELLED"}
|
|
93
|
+
meta = get_armature_extension(armature_data).vrm1.meta
|
|
94
|
+
author = meta.authors.add()
|
|
95
|
+
author.value = ""
|
|
96
|
+
meta.active_author_index = len(meta.authors) - 1
|
|
97
|
+
return {"FINISHED"}
|
|
98
|
+
|
|
99
|
+
if TYPE_CHECKING:
|
|
100
|
+
# This code is auto generated.
|
|
101
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
102
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class VRM_OT_remove_vrm1_meta_author(Operator):
|
|
106
|
+
bl_idname = "vrm.remove_vrm1_meta_author"
|
|
107
|
+
bl_label = "Remove Author"
|
|
108
|
+
bl_description = "Remove VRM 1.0 Meta Author"
|
|
109
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
110
|
+
|
|
111
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
112
|
+
options={"HIDDEN"},
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
author_index: IntProperty( # type: ignore[valid-type]
|
|
116
|
+
options={"HIDDEN"},
|
|
117
|
+
min=0,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
def execute(self, context: Context) -> set[str]:
|
|
121
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
122
|
+
if armature is None or armature.type != "ARMATURE":
|
|
123
|
+
return {"CANCELLED"}
|
|
124
|
+
armature_data = armature.data
|
|
125
|
+
if not isinstance(armature_data, Armature):
|
|
126
|
+
return {"CANCELLED"}
|
|
127
|
+
meta = get_armature_extension(armature_data).vrm1.meta
|
|
128
|
+
if len(meta.authors) <= self.author_index:
|
|
129
|
+
return {"CANCELLED"}
|
|
130
|
+
meta.authors.remove(self.author_index)
|
|
131
|
+
meta.active_author_index = min(
|
|
132
|
+
meta.active_author_index,
|
|
133
|
+
max(0, len(meta.authors) - 1),
|
|
134
|
+
)
|
|
135
|
+
return {"FINISHED"}
|
|
136
|
+
|
|
137
|
+
if TYPE_CHECKING:
|
|
138
|
+
# This code is auto generated.
|
|
139
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
140
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
141
|
+
author_index: int # type: ignore[no-redef]
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class VRM_OT_move_up_vrm1_meta_author(Operator):
|
|
145
|
+
bl_idname = "vrm.move_up_vrm1_meta_author"
|
|
146
|
+
bl_label = "Move Up Author"
|
|
147
|
+
bl_description = "Move Up VRM 1.0 Meta Author"
|
|
148
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
149
|
+
|
|
150
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
151
|
+
options={"HIDDEN"},
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
author_index: IntProperty( # type: ignore[valid-type]
|
|
155
|
+
options={"HIDDEN"},
|
|
156
|
+
min=0,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
def execute(self, context: Context) -> set[str]:
|
|
160
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
161
|
+
if armature is None or armature.type != "ARMATURE":
|
|
162
|
+
return {"CANCELLED"}
|
|
163
|
+
armature_data = armature.data
|
|
164
|
+
if not isinstance(armature_data, Armature):
|
|
165
|
+
return {"CANCELLED"}
|
|
166
|
+
meta = get_armature_extension(armature_data).vrm1.meta
|
|
167
|
+
if len(meta.authors) <= self.author_index:
|
|
168
|
+
return {"CANCELLED"}
|
|
169
|
+
new_index = (self.author_index - 1) % len(meta.authors)
|
|
170
|
+
meta.authors.move(self.author_index, new_index)
|
|
171
|
+
meta.active_author_index = new_index
|
|
172
|
+
return {"FINISHED"}
|
|
173
|
+
|
|
174
|
+
if TYPE_CHECKING:
|
|
175
|
+
# This code is auto generated.
|
|
176
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
177
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
178
|
+
author_index: int # type: ignore[no-redef]
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
class VRM_OT_move_down_vrm1_meta_author(Operator):
|
|
182
|
+
bl_idname = "vrm.move_down_vrm1_meta_author"
|
|
183
|
+
bl_label = "Move Down Author"
|
|
184
|
+
bl_description = "Move Down VRM 1.0 Meta Author"
|
|
185
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
186
|
+
|
|
187
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
188
|
+
options={"HIDDEN"},
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
author_index: IntProperty( # type: ignore[valid-type]
|
|
192
|
+
options={"HIDDEN"},
|
|
193
|
+
min=0,
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
def execute(self, context: Context) -> set[str]:
|
|
197
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
198
|
+
if armature is None or armature.type != "ARMATURE":
|
|
199
|
+
return {"CANCELLED"}
|
|
200
|
+
armature_data = armature.data
|
|
201
|
+
if not isinstance(armature_data, Armature):
|
|
202
|
+
return {"CANCELLED"}
|
|
203
|
+
meta = get_armature_extension(armature_data).vrm1.meta
|
|
204
|
+
if len(meta.authors) <= self.author_index:
|
|
205
|
+
return {"CANCELLED"}
|
|
206
|
+
new_index = (self.author_index + 1) % len(meta.authors)
|
|
207
|
+
meta.authors.move(self.author_index, new_index)
|
|
208
|
+
meta.active_author_index = new_index
|
|
209
|
+
return {"FINISHED"}
|
|
210
|
+
|
|
211
|
+
if TYPE_CHECKING:
|
|
212
|
+
# This code is auto generated.
|
|
213
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
214
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
215
|
+
author_index: int # type: ignore[no-redef]
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
class VRM_OT_add_vrm1_meta_reference(Operator):
|
|
219
|
+
bl_idname = "vrm.add_vrm1_meta_reference"
|
|
220
|
+
bl_label = "Add Reference"
|
|
221
|
+
bl_description = "Add VRM 1.0 Meta Reference"
|
|
222
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
223
|
+
|
|
224
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
225
|
+
options={"HIDDEN"},
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
def execute(self, context: Context) -> set[str]:
|
|
229
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
230
|
+
if armature is None or armature.type != "ARMATURE":
|
|
231
|
+
return {"CANCELLED"}
|
|
232
|
+
armature_data = armature.data
|
|
233
|
+
if not isinstance(armature_data, Armature):
|
|
234
|
+
return {"CANCELLED"}
|
|
235
|
+
meta = get_armature_extension(armature_data).vrm1.meta
|
|
236
|
+
reference = meta.references.add()
|
|
237
|
+
reference.value = ""
|
|
238
|
+
meta.active_reference_index = len(meta.references) - 1
|
|
239
|
+
return {"FINISHED"}
|
|
240
|
+
|
|
241
|
+
if TYPE_CHECKING:
|
|
242
|
+
# This code is auto generated.
|
|
243
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
244
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
class VRM_OT_remove_vrm1_meta_reference(Operator):
|
|
248
|
+
bl_idname = "vrm.remove_vrm1_meta_reference"
|
|
249
|
+
bl_label = "Remove Reference"
|
|
250
|
+
bl_description = "Remove VRM 1.0 Meta Reference"
|
|
251
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
252
|
+
|
|
253
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
254
|
+
options={"HIDDEN"},
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
reference_index: IntProperty( # type: ignore[valid-type]
|
|
258
|
+
options={"HIDDEN"},
|
|
259
|
+
min=0,
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
def execute(self, context: Context) -> set[str]:
|
|
263
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
264
|
+
if armature is None or armature.type != "ARMATURE":
|
|
265
|
+
return {"CANCELLED"}
|
|
266
|
+
armature_data = armature.data
|
|
267
|
+
if not isinstance(armature_data, Armature):
|
|
268
|
+
return {"CANCELLED"}
|
|
269
|
+
meta = get_armature_extension(armature_data).vrm1.meta
|
|
270
|
+
if len(meta.references) <= self.reference_index:
|
|
271
|
+
return {"CANCELLED"}
|
|
272
|
+
meta.references.remove(self.reference_index)
|
|
273
|
+
meta.active_reference_index = min(
|
|
274
|
+
meta.active_reference_index,
|
|
275
|
+
max(0, len(meta.references) - 1),
|
|
276
|
+
)
|
|
277
|
+
return {"FINISHED"}
|
|
278
|
+
|
|
279
|
+
if TYPE_CHECKING:
|
|
280
|
+
# This code is auto generated.
|
|
281
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
282
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
283
|
+
reference_index: int # type: ignore[no-redef]
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
class VRM_OT_move_up_vrm1_meta_reference(Operator):
|
|
287
|
+
bl_idname = "vrm.move_up_vrm1_meta_reference"
|
|
288
|
+
bl_label = "Move Up Reference"
|
|
289
|
+
bl_description = "Move Up VRM 1.0 Meta Reference"
|
|
290
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
291
|
+
|
|
292
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
293
|
+
options={"HIDDEN"},
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
reference_index: IntProperty( # type: ignore[valid-type]
|
|
297
|
+
options={"HIDDEN"},
|
|
298
|
+
min=0,
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
def execute(self, context: Context) -> set[str]:
|
|
302
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
303
|
+
if armature is None or armature.type != "ARMATURE":
|
|
304
|
+
return {"CANCELLED"}
|
|
305
|
+
armature_data = armature.data
|
|
306
|
+
if not isinstance(armature_data, Armature):
|
|
307
|
+
return {"CANCELLED"}
|
|
308
|
+
meta = get_armature_extension(armature_data).vrm1.meta
|
|
309
|
+
if len(meta.references) <= self.reference_index:
|
|
310
|
+
return {"CANCELLED"}
|
|
311
|
+
new_index = (self.reference_index - 1) % len(meta.references)
|
|
312
|
+
meta.references.move(self.reference_index, new_index)
|
|
313
|
+
meta.active_reference_index = new_index
|
|
314
|
+
return {"FINISHED"}
|
|
315
|
+
|
|
316
|
+
if TYPE_CHECKING:
|
|
317
|
+
# This code is auto generated.
|
|
318
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
319
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
320
|
+
reference_index: int # type: ignore[no-redef]
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
class VRM_OT_move_down_vrm1_meta_reference(Operator):
|
|
324
|
+
bl_idname = "vrm.move_down_vrm1_meta_reference"
|
|
325
|
+
bl_label = "Move Down Reference"
|
|
326
|
+
bl_description = "Move Down VRM 1.0 Meta Reference"
|
|
327
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
328
|
+
|
|
329
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
330
|
+
options={"HIDDEN"},
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
reference_index: IntProperty( # type: ignore[valid-type]
|
|
334
|
+
options={"HIDDEN"},
|
|
335
|
+
min=0,
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
def execute(self, context: Context) -> set[str]:
|
|
339
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
340
|
+
if armature is None or armature.type != "ARMATURE":
|
|
341
|
+
return {"CANCELLED"}
|
|
342
|
+
armature_data = armature.data
|
|
343
|
+
if not isinstance(armature_data, Armature):
|
|
344
|
+
return {"CANCELLED"}
|
|
345
|
+
meta = get_armature_extension(armature_data).vrm1.meta
|
|
346
|
+
if len(meta.references) <= self.reference_index:
|
|
347
|
+
return {"CANCELLED"}
|
|
348
|
+
new_index = (self.reference_index + 1) % len(meta.references)
|
|
349
|
+
meta.references.move(self.reference_index, new_index)
|
|
350
|
+
meta.active_reference_index = new_index
|
|
351
|
+
return {"FINISHED"}
|
|
352
|
+
|
|
353
|
+
if TYPE_CHECKING:
|
|
354
|
+
# This code is auto generated.
|
|
355
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
356
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
357
|
+
reference_index: int # type: ignore[no-redef]
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
class VRM_OT_add_vrm1_expressions_custom_expression(Operator):
|
|
361
|
+
bl_idname = "vrm.add_vrm1_expressions_custom_expression"
|
|
362
|
+
bl_label = "Add Custom Expression"
|
|
363
|
+
bl_description = "Add VRM 1.0 Custom Expression"
|
|
364
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
365
|
+
|
|
366
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
367
|
+
options={"HIDDEN"},
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
custom_expression_name: StringProperty( # type: ignore[valid-type]
|
|
371
|
+
options={"HIDDEN"},
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
def execute(self, context: Context) -> set[str]:
|
|
375
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
376
|
+
if armature is None or armature.type != "ARMATURE":
|
|
377
|
+
return {"CANCELLED"}
|
|
378
|
+
armature_data = armature.data
|
|
379
|
+
if not isinstance(armature_data, Armature):
|
|
380
|
+
return {"CANCELLED"}
|
|
381
|
+
expressions = get_armature_extension(armature_data).vrm1.expressions
|
|
382
|
+
new_last_custom_index = len(expressions.custom)
|
|
383
|
+
custom_expression = expressions.custom.add()
|
|
384
|
+
custom_expression.custom_name = self.custom_expression_name
|
|
385
|
+
expressions.active_expression_ui_list_element_index = (
|
|
386
|
+
len(expressions.preset.name_to_expression_dict()) + new_last_custom_index
|
|
387
|
+
)
|
|
388
|
+
update_vrm1_expression_ui_list_elements(context)
|
|
389
|
+
return {"FINISHED"}
|
|
390
|
+
|
|
391
|
+
if TYPE_CHECKING:
|
|
392
|
+
# This code is auto generated.
|
|
393
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
394
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
395
|
+
custom_expression_name: str # type: ignore[no-redef]
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
class VRM_OT_remove_vrm1_expressions_custom_expression(Operator):
|
|
399
|
+
bl_idname = "vrm.remove_vrm1_expressions_custom_expression"
|
|
400
|
+
bl_label = "Remove Custom Expression"
|
|
401
|
+
bl_description = "Remove VRM 1.0 Custom Expression"
|
|
402
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
403
|
+
|
|
404
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
405
|
+
options={"HIDDEN"},
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
custom_expression_name: StringProperty( # type: ignore[valid-type]
|
|
409
|
+
options={"HIDDEN"},
|
|
410
|
+
)
|
|
411
|
+
|
|
412
|
+
def execute(self, context: Context) -> set[str]:
|
|
413
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
414
|
+
if armature is None or armature.type != "ARMATURE":
|
|
415
|
+
return {"CANCELLED"}
|
|
416
|
+
armature_data = armature.data
|
|
417
|
+
if not isinstance(armature_data, Armature):
|
|
418
|
+
return {"CANCELLED"}
|
|
419
|
+
expressions = get_armature_extension(armature_data).vrm1.expressions
|
|
420
|
+
for custom_index, custom_expression in enumerate(
|
|
421
|
+
list(expressions.custom.values())
|
|
422
|
+
):
|
|
423
|
+
if custom_expression.custom_name == self.custom_expression_name:
|
|
424
|
+
expressions.custom.remove(custom_index)
|
|
425
|
+
expressions.active_expression_ui_list_element_index = min(
|
|
426
|
+
expressions.active_expression_ui_list_element_index,
|
|
427
|
+
len(expressions.all_name_to_expression_dict()) - 1,
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
update_vrm1_expression_ui_list_elements(context)
|
|
431
|
+
return {"FINISHED"}
|
|
432
|
+
return {"CANCELLED"}
|
|
433
|
+
|
|
434
|
+
if TYPE_CHECKING:
|
|
435
|
+
# This code is auto generated.
|
|
436
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
437
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
438
|
+
custom_expression_name: str # type: ignore[no-redef]
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
class VRM_OT_move_up_vrm1_expressions_custom_expression(Operator):
|
|
442
|
+
bl_idname = "vrm.move_up_vrm1_expressions_custom_expression"
|
|
443
|
+
bl_label = "Move Up Custom Expression"
|
|
444
|
+
bl_description = "Move Up VRM 1.0 Custom Expression"
|
|
445
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
446
|
+
|
|
447
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
448
|
+
options={"HIDDEN"},
|
|
449
|
+
)
|
|
450
|
+
|
|
451
|
+
custom_expression_name: StringProperty( # type: ignore[valid-type]
|
|
452
|
+
options={"HIDDEN"},
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
def execute(self, context: Context) -> set[str]:
|
|
456
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
457
|
+
if armature is None or armature.type != "ARMATURE":
|
|
458
|
+
return {"CANCELLED"}
|
|
459
|
+
armature_data = armature.data
|
|
460
|
+
if not isinstance(armature_data, Armature):
|
|
461
|
+
return {"CANCELLED"}
|
|
462
|
+
expressions = get_armature_extension(armature_data).vrm1.expressions
|
|
463
|
+
expression_index = next(
|
|
464
|
+
(
|
|
465
|
+
i
|
|
466
|
+
for i, expression in enumerate(expressions.custom)
|
|
467
|
+
if expression.custom_name == self.custom_expression_name
|
|
468
|
+
),
|
|
469
|
+
None,
|
|
470
|
+
)
|
|
471
|
+
if expression_index is None:
|
|
472
|
+
return {"CANCELLED"}
|
|
473
|
+
new_expression_index = (expression_index - 1) % len(expressions.custom)
|
|
474
|
+
expressions.custom.move(expression_index, new_expression_index)
|
|
475
|
+
expressions.active_expression_ui_list_element_index = (
|
|
476
|
+
len(expressions.preset.name_to_expression_dict()) + new_expression_index
|
|
477
|
+
)
|
|
478
|
+
return {"FINISHED"}
|
|
479
|
+
|
|
480
|
+
if TYPE_CHECKING:
|
|
481
|
+
# This code is auto generated.
|
|
482
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
483
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
484
|
+
custom_expression_name: str # type: ignore[no-redef]
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
class VRM_OT_move_down_vrm1_expressions_custom_expression(Operator):
|
|
488
|
+
bl_idname = "vrm.move_down_vrm1_expressions_custom_expression"
|
|
489
|
+
bl_label = "Move Down Custom Expression"
|
|
490
|
+
bl_description = "Move Down VRM 1.0 Custom Expression"
|
|
491
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
492
|
+
|
|
493
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
494
|
+
options={"HIDDEN"},
|
|
495
|
+
)
|
|
496
|
+
|
|
497
|
+
custom_expression_name: StringProperty( # type: ignore[valid-type]
|
|
498
|
+
options={"HIDDEN"},
|
|
499
|
+
)
|
|
500
|
+
|
|
501
|
+
def execute(self, context: Context) -> set[str]:
|
|
502
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
503
|
+
if armature is None or armature.type != "ARMATURE":
|
|
504
|
+
return {"CANCELLED"}
|
|
505
|
+
armature_data = armature.data
|
|
506
|
+
if not isinstance(armature_data, Armature):
|
|
507
|
+
return {"CANCELLED"}
|
|
508
|
+
expressions = get_armature_extension(armature_data).vrm1.expressions
|
|
509
|
+
expression_index = next(
|
|
510
|
+
(
|
|
511
|
+
i
|
|
512
|
+
for i, expression in enumerate(expressions.custom)
|
|
513
|
+
if expression.custom_name == self.custom_expression_name
|
|
514
|
+
),
|
|
515
|
+
None,
|
|
516
|
+
)
|
|
517
|
+
if expression_index is None:
|
|
518
|
+
return {"CANCELLED"}
|
|
519
|
+
new_expression_index = (expression_index + 1) % len(expressions.custom)
|
|
520
|
+
expressions.custom.move(expression_index, new_expression_index)
|
|
521
|
+
expressions.active_expression_ui_list_element_index = (
|
|
522
|
+
len(expressions.preset.name_to_expression_dict()) + new_expression_index
|
|
523
|
+
)
|
|
524
|
+
return {"FINISHED"}
|
|
525
|
+
|
|
526
|
+
if TYPE_CHECKING:
|
|
527
|
+
# This code is auto generated.
|
|
528
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
529
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
530
|
+
custom_expression_name: str # type: ignore[no-redef]
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
class VRM_OT_add_vrm1_first_person_mesh_annotation(Operator):
|
|
534
|
+
bl_idname = "vrm.add_vrm1_first_person_mesh_annotation"
|
|
535
|
+
bl_label = "Add Mesh Annotation"
|
|
536
|
+
bl_description = "Add VRM 1.0 First Person Mesh Annotation"
|
|
537
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
538
|
+
|
|
539
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
540
|
+
options={"HIDDEN"},
|
|
541
|
+
)
|
|
542
|
+
|
|
543
|
+
def execute(self, context: Context) -> set[str]:
|
|
544
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
545
|
+
if armature is None or armature.type != "ARMATURE":
|
|
546
|
+
return {"CANCELLED"}
|
|
547
|
+
armature_data = armature.data
|
|
548
|
+
if not isinstance(armature_data, Armature):
|
|
549
|
+
return {"CANCELLED"}
|
|
550
|
+
first_person = get_armature_extension(armature_data).vrm1.first_person
|
|
551
|
+
first_person.mesh_annotations.add()
|
|
552
|
+
first_person.active_mesh_annotation_index = (
|
|
553
|
+
len(first_person.mesh_annotations) - 1
|
|
554
|
+
)
|
|
555
|
+
return {"FINISHED"}
|
|
556
|
+
|
|
557
|
+
if TYPE_CHECKING:
|
|
558
|
+
# This code is auto generated.
|
|
559
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
560
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
class VRM_OT_remove_vrm1_first_person_mesh_annotation(Operator):
|
|
564
|
+
bl_idname = "vrm.remove_vrm1_first_person_mesh_annotation"
|
|
565
|
+
bl_label = "Remove Mesh Annotation"
|
|
566
|
+
bl_description = "Remove VRM 1.0 First Person Mesh Annotation"
|
|
567
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
568
|
+
|
|
569
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
570
|
+
options={"HIDDEN"},
|
|
571
|
+
)
|
|
572
|
+
|
|
573
|
+
mesh_annotation_index: IntProperty( # type: ignore[valid-type]
|
|
574
|
+
options={"HIDDEN"},
|
|
575
|
+
min=0,
|
|
576
|
+
)
|
|
577
|
+
|
|
578
|
+
def execute(self, context: Context) -> set[str]:
|
|
579
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
580
|
+
if armature is None or armature.type != "ARMATURE":
|
|
581
|
+
return {"CANCELLED"}
|
|
582
|
+
armature_data = armature.data
|
|
583
|
+
if not isinstance(armature_data, Armature):
|
|
584
|
+
return {"CANCELLED"}
|
|
585
|
+
first_person = get_armature_extension(armature_data).vrm1.first_person
|
|
586
|
+
if len(first_person.mesh_annotations) <= self.mesh_annotation_index:
|
|
587
|
+
return {"CANCELLED"}
|
|
588
|
+
first_person.mesh_annotations.remove(self.mesh_annotation_index)
|
|
589
|
+
first_person.active_mesh_annotation_index = min(
|
|
590
|
+
first_person.active_mesh_annotation_index,
|
|
591
|
+
max(0, len(first_person.mesh_annotations) - 1),
|
|
592
|
+
)
|
|
593
|
+
return {"FINISHED"}
|
|
594
|
+
|
|
595
|
+
if TYPE_CHECKING:
|
|
596
|
+
# This code is auto generated.
|
|
597
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
598
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
599
|
+
mesh_annotation_index: int # type: ignore[no-redef]
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
class VRM_OT_move_up_vrm1_first_person_mesh_annotation(Operator):
|
|
603
|
+
bl_idname = "vrm.move_up_vrm1_first_person_mesh_annotation"
|
|
604
|
+
bl_label = "Move Up Mesh Annotation"
|
|
605
|
+
bl_description = "Move Up VRM 1.0 First Person Mesh Annotation"
|
|
606
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
607
|
+
|
|
608
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
609
|
+
options={"HIDDEN"},
|
|
610
|
+
)
|
|
611
|
+
|
|
612
|
+
mesh_annotation_index: IntProperty( # type: ignore[valid-type]
|
|
613
|
+
options={"HIDDEN"},
|
|
614
|
+
min=0,
|
|
615
|
+
)
|
|
616
|
+
|
|
617
|
+
def execute(self, context: Context) -> set[str]:
|
|
618
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
619
|
+
if armature is None or armature.type != "ARMATURE":
|
|
620
|
+
return {"CANCELLED"}
|
|
621
|
+
armature_data = armature.data
|
|
622
|
+
if not isinstance(armature_data, Armature):
|
|
623
|
+
return {"CANCELLED"}
|
|
624
|
+
first_person = get_armature_extension(armature_data).vrm1.first_person
|
|
625
|
+
if len(first_person.mesh_annotations) <= self.mesh_annotation_index:
|
|
626
|
+
return {"CANCELLED"}
|
|
627
|
+
new_index = (self.mesh_annotation_index - 1) % len(
|
|
628
|
+
first_person.mesh_annotations
|
|
629
|
+
)
|
|
630
|
+
first_person.mesh_annotations.move(self.mesh_annotation_index, new_index)
|
|
631
|
+
first_person.active_mesh_annotation_index = new_index
|
|
632
|
+
return {"FINISHED"}
|
|
633
|
+
|
|
634
|
+
if TYPE_CHECKING:
|
|
635
|
+
# This code is auto generated.
|
|
636
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
637
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
638
|
+
mesh_annotation_index: int # type: ignore[no-redef]
|
|
639
|
+
|
|
640
|
+
|
|
641
|
+
class VRM_OT_move_down_vrm1_first_person_mesh_annotation(Operator):
|
|
642
|
+
bl_idname = "vrm.move_down_vrm1_first_person_mesh_annotation"
|
|
643
|
+
bl_label = "Move Down Mesh Annotation"
|
|
644
|
+
bl_description = "Move Down VRM 1.0 First Person Mesh Annotation"
|
|
645
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
646
|
+
|
|
647
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
648
|
+
options={"HIDDEN"},
|
|
649
|
+
)
|
|
650
|
+
|
|
651
|
+
mesh_annotation_index: IntProperty( # type: ignore[valid-type]
|
|
652
|
+
options={"HIDDEN"},
|
|
653
|
+
min=0,
|
|
654
|
+
)
|
|
655
|
+
|
|
656
|
+
def execute(self, context: Context) -> set[str]:
|
|
657
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
658
|
+
if armature is None or armature.type != "ARMATURE":
|
|
659
|
+
return {"CANCELLED"}
|
|
660
|
+
armature_data = armature.data
|
|
661
|
+
if not isinstance(armature_data, Armature):
|
|
662
|
+
return {"CANCELLED"}
|
|
663
|
+
first_person = get_armature_extension(armature_data).vrm1.first_person
|
|
664
|
+
if len(first_person.mesh_annotations) <= self.mesh_annotation_index:
|
|
665
|
+
return {"CANCELLED"}
|
|
666
|
+
new_index = (self.mesh_annotation_index + 1) % len(
|
|
667
|
+
first_person.mesh_annotations
|
|
668
|
+
)
|
|
669
|
+
first_person.mesh_annotations.move(self.mesh_annotation_index, new_index)
|
|
670
|
+
first_person.active_mesh_annotation_index = new_index
|
|
671
|
+
return {"FINISHED"}
|
|
672
|
+
|
|
673
|
+
if TYPE_CHECKING:
|
|
674
|
+
# This code is auto generated.
|
|
675
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
676
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
677
|
+
mesh_annotation_index: int # type: ignore[no-redef]
|
|
678
|
+
|
|
679
|
+
|
|
680
|
+
class VRM_OT_add_vrm1_expression_morph_target_bind(Operator):
|
|
681
|
+
bl_idname = "vrm.add_vrm1_expression_morph_target_bind"
|
|
682
|
+
bl_label = "Add Morph Target Bind"
|
|
683
|
+
bl_description = "Add VRM 1.0 Expression Morph Target Bind"
|
|
684
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
685
|
+
|
|
686
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
687
|
+
options={"HIDDEN"},
|
|
688
|
+
)
|
|
689
|
+
|
|
690
|
+
expression_name: StringProperty( # type: ignore[valid-type]
|
|
691
|
+
options={"HIDDEN"},
|
|
692
|
+
)
|
|
693
|
+
|
|
694
|
+
def execute(self, context: Context) -> set[str]:
|
|
695
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
696
|
+
if armature is None or armature.type != "ARMATURE":
|
|
697
|
+
return {"CANCELLED"}
|
|
698
|
+
armature_data = armature.data
|
|
699
|
+
if not isinstance(armature_data, Armature):
|
|
700
|
+
return {"CANCELLED"}
|
|
701
|
+
expressions = get_armature_extension(armature_data).vrm1.expressions
|
|
702
|
+
expression = expressions.all_name_to_expression_dict().get(self.expression_name)
|
|
703
|
+
if expression is None:
|
|
704
|
+
return {"CANCELLED"}
|
|
705
|
+
expression.morph_target_binds.add()
|
|
706
|
+
expression.active_morph_target_bind_index = (
|
|
707
|
+
len(expression.morph_target_binds) - 1
|
|
708
|
+
)
|
|
709
|
+
return {"FINISHED"}
|
|
710
|
+
|
|
711
|
+
if TYPE_CHECKING:
|
|
712
|
+
# This code is auto generated.
|
|
713
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
714
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
715
|
+
expression_name: str # type: ignore[no-redef]
|
|
716
|
+
|
|
717
|
+
|
|
718
|
+
class VRM_OT_remove_vrm1_expression_morph_target_bind(Operator):
|
|
719
|
+
bl_idname = "vrm.remove_vrm1_expression_morph_target_bind"
|
|
720
|
+
bl_label = "Remove Morph Target Bind"
|
|
721
|
+
bl_description = "Remove VRM 1.0 Expression Morph Target Bind"
|
|
722
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
723
|
+
|
|
724
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
725
|
+
options={"HIDDEN"},
|
|
726
|
+
)
|
|
727
|
+
|
|
728
|
+
expression_name: StringProperty( # type: ignore[valid-type]
|
|
729
|
+
options={"HIDDEN"},
|
|
730
|
+
)
|
|
731
|
+
bind_index: IntProperty( # type: ignore[valid-type]
|
|
732
|
+
options={"HIDDEN"},
|
|
733
|
+
min=0,
|
|
734
|
+
)
|
|
735
|
+
|
|
736
|
+
def execute(self, context: Context) -> set[str]:
|
|
737
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
738
|
+
if armature is None or armature.type != "ARMATURE":
|
|
739
|
+
return {"CANCELLED"}
|
|
740
|
+
armature_data = armature.data
|
|
741
|
+
if not isinstance(armature_data, Armature):
|
|
742
|
+
return {"CANCELLED"}
|
|
743
|
+
expressions = get_armature_extension(armature_data).vrm1.expressions
|
|
744
|
+
expression = expressions.all_name_to_expression_dict().get(self.expression_name)
|
|
745
|
+
if expression is None:
|
|
746
|
+
return {"CANCELLED"}
|
|
747
|
+
if len(expression.morph_target_binds) <= self.bind_index:
|
|
748
|
+
return {"CANCELLED"}
|
|
749
|
+
expression.morph_target_binds.remove(self.bind_index)
|
|
750
|
+
expression.active_morph_target_bind_index = min(
|
|
751
|
+
expression.active_morph_target_bind_index,
|
|
752
|
+
max(0, len(expression.morph_target_binds) - 1),
|
|
753
|
+
)
|
|
754
|
+
return {"FINISHED"}
|
|
755
|
+
|
|
756
|
+
if TYPE_CHECKING:
|
|
757
|
+
# This code is auto generated.
|
|
758
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
759
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
760
|
+
expression_name: str # type: ignore[no-redef]
|
|
761
|
+
bind_index: int # type: ignore[no-redef]
|
|
762
|
+
|
|
763
|
+
|
|
764
|
+
class VRM_OT_move_up_vrm1_expression_morph_target_bind(Operator):
|
|
765
|
+
bl_idname = "vrm.move_up_vrm1_expression_morph_target_bind"
|
|
766
|
+
bl_label = "Move Up Morph Target Bind"
|
|
767
|
+
bl_description = "Move Up VRM 1.0 Expression Morph Target Bind"
|
|
768
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
769
|
+
|
|
770
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
771
|
+
options={"HIDDEN"},
|
|
772
|
+
)
|
|
773
|
+
|
|
774
|
+
expression_name: StringProperty( # type: ignore[valid-type]
|
|
775
|
+
options={"HIDDEN"},
|
|
776
|
+
)
|
|
777
|
+
bind_index: IntProperty( # type: ignore[valid-type]
|
|
778
|
+
options={"HIDDEN"},
|
|
779
|
+
min=0,
|
|
780
|
+
)
|
|
781
|
+
|
|
782
|
+
def execute(self, context: Context) -> set[str]:
|
|
783
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
784
|
+
if armature is None or armature.type != "ARMATURE":
|
|
785
|
+
return {"CANCELLED"}
|
|
786
|
+
armature_data = armature.data
|
|
787
|
+
if not isinstance(armature_data, Armature):
|
|
788
|
+
return {"CANCELLED"}
|
|
789
|
+
expressions = get_armature_extension(armature_data).vrm1.expressions
|
|
790
|
+
expression = expressions.all_name_to_expression_dict().get(self.expression_name)
|
|
791
|
+
if expression is None:
|
|
792
|
+
return {"CANCELLED"}
|
|
793
|
+
if len(expression.morph_target_binds) <= self.bind_index:
|
|
794
|
+
return {"CANCELLED"}
|
|
795
|
+
new_bind_index = (self.bind_index - 1) % len(expression.morph_target_binds)
|
|
796
|
+
expression.morph_target_binds.move(self.bind_index, new_bind_index)
|
|
797
|
+
expression.active_morph_target_bind_index = new_bind_index
|
|
798
|
+
return {"FINISHED"}
|
|
799
|
+
|
|
800
|
+
if TYPE_CHECKING:
|
|
801
|
+
# This code is auto generated.
|
|
802
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
803
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
804
|
+
expression_name: str # type: ignore[no-redef]
|
|
805
|
+
bind_index: int # type: ignore[no-redef]
|
|
806
|
+
|
|
807
|
+
|
|
808
|
+
class VRM_OT_move_down_vrm1_expression_morph_target_bind(Operator):
|
|
809
|
+
bl_idname = "vrm.move_down_vrm1_expression_morph_target_bind"
|
|
810
|
+
bl_label = "Move Down Morph Target Bind"
|
|
811
|
+
bl_description = "Move Down VRM 1.0 Expression Morph Target Bind"
|
|
812
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
813
|
+
|
|
814
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
815
|
+
options={"HIDDEN"},
|
|
816
|
+
)
|
|
817
|
+
|
|
818
|
+
expression_name: StringProperty( # type: ignore[valid-type]
|
|
819
|
+
options={"HIDDEN"},
|
|
820
|
+
)
|
|
821
|
+
bind_index: IntProperty( # type: ignore[valid-type]
|
|
822
|
+
options={"HIDDEN"},
|
|
823
|
+
min=0,
|
|
824
|
+
)
|
|
825
|
+
|
|
826
|
+
def execute(self, context: Context) -> set[str]:
|
|
827
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
828
|
+
if armature is None or armature.type != "ARMATURE":
|
|
829
|
+
return {"CANCELLED"}
|
|
830
|
+
armature_data = armature.data
|
|
831
|
+
if not isinstance(armature_data, Armature):
|
|
832
|
+
return {"CANCELLED"}
|
|
833
|
+
expressions = get_armature_extension(armature_data).vrm1.expressions
|
|
834
|
+
expression = expressions.all_name_to_expression_dict().get(self.expression_name)
|
|
835
|
+
if expression is None:
|
|
836
|
+
return {"CANCELLED"}
|
|
837
|
+
if len(expression.morph_target_binds) <= self.bind_index:
|
|
838
|
+
return {"CANCELLED"}
|
|
839
|
+
new_bind_index = (self.bind_index + 1) % len(expression.morph_target_binds)
|
|
840
|
+
expression.morph_target_binds.move(self.bind_index, new_bind_index)
|
|
841
|
+
expression.active_morph_target_bind_index = new_bind_index
|
|
842
|
+
return {"FINISHED"}
|
|
843
|
+
|
|
844
|
+
if TYPE_CHECKING:
|
|
845
|
+
# This code is auto generated.
|
|
846
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
847
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
848
|
+
expression_name: str # type: ignore[no-redef]
|
|
849
|
+
bind_index: int # type: ignore[no-redef]
|
|
850
|
+
|
|
851
|
+
|
|
852
|
+
class VRM_OT_restore_vrm1_expression_morph_target_bind_object(Operator):
|
|
853
|
+
bl_idname = "vrm.restore_vrm1_expression_morph_target_bind_object"
|
|
854
|
+
bl_label = "Restore Shape Key Assignments"
|
|
855
|
+
bl_description = "Restore VRM 1.0 Expression Morph Target Bind Object Assignments"
|
|
856
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
857
|
+
|
|
858
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
859
|
+
options={"HIDDEN"},
|
|
860
|
+
)
|
|
861
|
+
|
|
862
|
+
def execute(self, context: Context) -> set[str]:
|
|
863
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
864
|
+
if armature is None or armature.type != "ARMATURE":
|
|
865
|
+
return {"CANCELLED"}
|
|
866
|
+
armature_data = armature.data
|
|
867
|
+
if not isinstance(armature_data, Armature):
|
|
868
|
+
return {"CANCELLED"}
|
|
869
|
+
expressions = get_armature_extension(armature_data).vrm1.expressions
|
|
870
|
+
expressions.restore_expression_morph_target_bind_object_assignments(context)
|
|
871
|
+
return {"FINISHED"}
|
|
872
|
+
|
|
873
|
+
if TYPE_CHECKING:
|
|
874
|
+
# This code is auto generated.
|
|
875
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
876
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
877
|
+
|
|
878
|
+
|
|
879
|
+
def _add_shape_keys_to_vrm1_expressions(
|
|
880
|
+
context: Context,
|
|
881
|
+
armature_object_name: str,
|
|
882
|
+
shape_key_mapping: Mapping[ExpressionPreset, Sequence[Mapping[str, float]]],
|
|
883
|
+
) -> set[str]:
|
|
884
|
+
armature = context.blend_data.objects.get(armature_object_name)
|
|
885
|
+
if armature is None or armature.type != "ARMATURE":
|
|
886
|
+
return {"CANCELLED"}
|
|
887
|
+
armature_data = armature.data
|
|
888
|
+
if not isinstance(armature_data, Armature):
|
|
889
|
+
return {"CANCELLED"}
|
|
890
|
+
|
|
891
|
+
mesh_object_name_and_key_block_names = [
|
|
892
|
+
(obj.name, key_block.name)
|
|
893
|
+
for obj in search.export_objects(
|
|
894
|
+
context,
|
|
895
|
+
armature_object_name,
|
|
896
|
+
export_invisibles=True,
|
|
897
|
+
export_only_selections=False,
|
|
898
|
+
export_lights=False,
|
|
899
|
+
)
|
|
900
|
+
if isinstance(mesh_data := obj.data, Mesh)
|
|
901
|
+
and (shape_keys := mesh_data.shape_keys)
|
|
902
|
+
and (key_blocks := shape_keys.key_blocks)
|
|
903
|
+
for key_block in key_blocks
|
|
904
|
+
]
|
|
905
|
+
existing_key_block_names = [
|
|
906
|
+
key_block_name for _, key_block_name in mesh_object_name_and_key_block_names
|
|
907
|
+
]
|
|
908
|
+
expressions = get_armature_extension(armature_data).vrm1.expressions
|
|
909
|
+
for expression_preset, key_block_name_to_weights in shape_key_mapping.items():
|
|
910
|
+
expression = next(
|
|
911
|
+
(
|
|
912
|
+
expression
|
|
913
|
+
for (
|
|
914
|
+
searching_preset,
|
|
915
|
+
expression,
|
|
916
|
+
) in expressions.preset.expression_preset_and_expressions()
|
|
917
|
+
if searching_preset == expression_preset
|
|
918
|
+
),
|
|
919
|
+
None,
|
|
920
|
+
)
|
|
921
|
+
if expression is None:
|
|
922
|
+
continue
|
|
923
|
+
|
|
924
|
+
for match_all in (True, False):
|
|
925
|
+
for key_block_name_to_weight in key_block_name_to_weights:
|
|
926
|
+
if expression.morph_target_binds:
|
|
927
|
+
continue
|
|
928
|
+
|
|
929
|
+
if match_all and not all(
|
|
930
|
+
key_block_name in existing_key_block_names
|
|
931
|
+
for key_block_name in key_block_name_to_weight.keys()
|
|
932
|
+
):
|
|
933
|
+
continue
|
|
934
|
+
|
|
935
|
+
for mesh_object_name, key_block_name, weight in (
|
|
936
|
+
(mesh_object_name, key_block_name, weight)
|
|
937
|
+
for key_block_name, weight in key_block_name_to_weight.items()
|
|
938
|
+
for mesh_object_name, searching_key_block_name in (
|
|
939
|
+
mesh_object_name_and_key_block_names
|
|
940
|
+
)
|
|
941
|
+
if key_block_name == searching_key_block_name
|
|
942
|
+
):
|
|
943
|
+
morph_target_bind = expression.morph_target_binds.add()
|
|
944
|
+
morph_target_bind.node.mesh_object_name = mesh_object_name
|
|
945
|
+
morph_target_bind.index = key_block_name
|
|
946
|
+
morph_target_bind.weight = weight
|
|
947
|
+
|
|
948
|
+
update_vrm1_expression_ui_list_elements(context)
|
|
949
|
+
return {"FINISHED"}
|
|
950
|
+
|
|
951
|
+
|
|
952
|
+
def assign_vrm1_expressions_from_vrm0(
|
|
953
|
+
context: Context, armature_object_name: str
|
|
954
|
+
) -> set[str]:
|
|
955
|
+
armature = context.blend_data.objects.get(armature_object_name)
|
|
956
|
+
if armature is None or armature.type != "ARMATURE":
|
|
957
|
+
return {"CANCELLED"}
|
|
958
|
+
armature_data = armature.data
|
|
959
|
+
if not isinstance(armature_data, Armature):
|
|
960
|
+
return {"CANCELLED"}
|
|
961
|
+
|
|
962
|
+
expressions = get_armature_extension(armature_data).vrm1.expressions
|
|
963
|
+
blend_shape_groups = get_armature_extension(
|
|
964
|
+
armature_data
|
|
965
|
+
).vrm0.blend_shape_master.blend_shape_groups
|
|
966
|
+
|
|
967
|
+
for blend_shape_group in blend_shape_groups:
|
|
968
|
+
if not blend_shape_group.name:
|
|
969
|
+
continue
|
|
970
|
+
|
|
971
|
+
if (
|
|
972
|
+
blend_shape_group.preset_name
|
|
973
|
+
== blend_shape_group.PRESET_NAME_UNKNOWN.identifier
|
|
974
|
+
):
|
|
975
|
+
if blend_shape_group.name.casefold() in {
|
|
976
|
+
"surprise",
|
|
977
|
+
"surprised",
|
|
978
|
+
}:
|
|
979
|
+
expression_preset = ExpressionPresets.SURPRISED
|
|
980
|
+
else:
|
|
981
|
+
expression_preset = None
|
|
982
|
+
else:
|
|
983
|
+
expression_preset = next(
|
|
984
|
+
(
|
|
985
|
+
expression_preset
|
|
986
|
+
for expression_preset in ExpressionPresets.all
|
|
987
|
+
if blend_shape_group.preset_name == expression_preset.vrm0_preset
|
|
988
|
+
),
|
|
989
|
+
None,
|
|
990
|
+
)
|
|
991
|
+
|
|
992
|
+
if expression_preset is None:
|
|
993
|
+
expression = next(
|
|
994
|
+
(
|
|
995
|
+
custom_expression
|
|
996
|
+
for custom_expression in expressions.custom
|
|
997
|
+
if custom_expression.custom_name == blend_shape_group.name
|
|
998
|
+
),
|
|
999
|
+
None,
|
|
1000
|
+
)
|
|
1001
|
+
if expression is None:
|
|
1002
|
+
expression = expressions.custom.add()
|
|
1003
|
+
expression.custom_name = blend_shape_group.name
|
|
1004
|
+
else:
|
|
1005
|
+
expression = expressions.preset.name_to_expression_dict().get(
|
|
1006
|
+
expression_preset.name
|
|
1007
|
+
)
|
|
1008
|
+
if expression is None:
|
|
1009
|
+
continue
|
|
1010
|
+
|
|
1011
|
+
if (
|
|
1012
|
+
expression.morph_target_binds
|
|
1013
|
+
or expression.material_color_binds
|
|
1014
|
+
or expression.texture_transform_binds
|
|
1015
|
+
):
|
|
1016
|
+
continue
|
|
1017
|
+
|
|
1018
|
+
for bind in blend_shape_group.binds:
|
|
1019
|
+
mesh_object_name = bind.mesh.mesh_object_name
|
|
1020
|
+
if not mesh_object_name:
|
|
1021
|
+
continue
|
|
1022
|
+
morph_target_bind = expression.morph_target_binds.add()
|
|
1023
|
+
morph_target_bind.node.mesh_object_name = mesh_object_name
|
|
1024
|
+
morph_target_bind.index = bind.index
|
|
1025
|
+
morph_target_bind.weight = bind.weight
|
|
1026
|
+
|
|
1027
|
+
for material_value in blend_shape_group.material_values:
|
|
1028
|
+
material = material_value.material
|
|
1029
|
+
if not material:
|
|
1030
|
+
continue
|
|
1031
|
+
|
|
1032
|
+
is_mtoon = get_material_extension(material).mtoon1.enabled or (
|
|
1033
|
+
(legacy_addon_material := LegacyAddonMaterial.try_parse(material))
|
|
1034
|
+
and legacy_addon_material.shader_name == "MToon_unversioned"
|
|
1035
|
+
)
|
|
1036
|
+
material_property = next(
|
|
1037
|
+
(
|
|
1038
|
+
material_property
|
|
1039
|
+
for material_property in (
|
|
1040
|
+
list(MTOON0_PROPERTIES.values())
|
|
1041
|
+
+ list(GLTF_PROPERTIES.values())
|
|
1042
|
+
if is_mtoon
|
|
1043
|
+
else list(GLTF_PROPERTIES.values())
|
|
1044
|
+
+ list(MTOON0_PROPERTIES.values())
|
|
1045
|
+
)
|
|
1046
|
+
if material_property.name == material_value.property_name
|
|
1047
|
+
),
|
|
1048
|
+
None,
|
|
1049
|
+
)
|
|
1050
|
+
if not material_property:
|
|
1051
|
+
continue
|
|
1052
|
+
material_property_target = material_property.target
|
|
1053
|
+
if material_property_target is None:
|
|
1054
|
+
continue
|
|
1055
|
+
|
|
1056
|
+
if material_property.type in {
|
|
1057
|
+
MaterialPropertyType.RGB,
|
|
1058
|
+
MaterialPropertyType.RGBA,
|
|
1059
|
+
} and (
|
|
1060
|
+
material_color_bind_type := {
|
|
1061
|
+
MaterialPropertyTarget.COLOR: (
|
|
1062
|
+
Vrm1MaterialColorBindPropertyGroup.TYPE_COLOR
|
|
1063
|
+
),
|
|
1064
|
+
MaterialPropertyTarget.EMISSION_COLOR: (
|
|
1065
|
+
Vrm1MaterialColorBindPropertyGroup.TYPE_EMISSION_COLOR
|
|
1066
|
+
),
|
|
1067
|
+
MaterialPropertyTarget.SHADE_COLOR: (
|
|
1068
|
+
Vrm1MaterialColorBindPropertyGroup.TYPE_SHADE_COLOR
|
|
1069
|
+
),
|
|
1070
|
+
# No SPHERE_ADD_COLOR
|
|
1071
|
+
# MaterialPropertyTarget.SPHERE_ADD: (
|
|
1072
|
+
# Vrm1MaterialColorBindPropertyGroup.TYPE_MATCAP_COLOR
|
|
1073
|
+
# )
|
|
1074
|
+
MaterialPropertyTarget.RIM_COLOR: (
|
|
1075
|
+
Vrm1MaterialColorBindPropertyGroup.TYPE_RIM_COLOR
|
|
1076
|
+
),
|
|
1077
|
+
MaterialPropertyTarget.OUTLINE_COLOR: (
|
|
1078
|
+
Vrm1MaterialColorBindPropertyGroup.TYPE_OUTLINE_COLOR
|
|
1079
|
+
),
|
|
1080
|
+
}.get(material_property_target)
|
|
1081
|
+
):
|
|
1082
|
+
material_color_bind = expression.material_color_binds.add()
|
|
1083
|
+
material_color_bind.material = material
|
|
1084
|
+
material_color_bind.type = material_color_bind_type.identifier
|
|
1085
|
+
if (
|
|
1086
|
+
material_color_bind_type.identifier
|
|
1087
|
+
== Vrm1MaterialColorBindPropertyGroup.TYPE_COLOR.identifier
|
|
1088
|
+
):
|
|
1089
|
+
material_color_bind.target_value = (
|
|
1090
|
+
material_value.target_value_as_rgba
|
|
1091
|
+
)
|
|
1092
|
+
else:
|
|
1093
|
+
material_color_bind.target_value_as_rgb = (
|
|
1094
|
+
material_value.target_value_as_rgb
|
|
1095
|
+
)
|
|
1096
|
+
|
|
1097
|
+
if (
|
|
1098
|
+
material_property.type
|
|
1099
|
+
in {
|
|
1100
|
+
MaterialPropertyType.UV,
|
|
1101
|
+
MaterialPropertyType.UV_S,
|
|
1102
|
+
MaterialPropertyType.UV_T,
|
|
1103
|
+
}
|
|
1104
|
+
and material_property_target == MaterialPropertyTarget.MAIN_TEX
|
|
1105
|
+
):
|
|
1106
|
+
texture_transform_bind = expression.texture_transform_binds.add()
|
|
1107
|
+
texture_transform_bind.material = material
|
|
1108
|
+
texture_transform_bind.scale = (
|
|
1109
|
+
material_value.target_value_tiling_s,
|
|
1110
|
+
material_value.target_value_tiling_t,
|
|
1111
|
+
)
|
|
1112
|
+
texture_transform_bind.offset = (
|
|
1113
|
+
material_value.target_value_offset_s,
|
|
1114
|
+
# https://github.com/vrm-c/UniVRM/issues/930
|
|
1115
|
+
1
|
|
1116
|
+
- material_value.target_value_offset_t
|
|
1117
|
+
- material_value.target_value_tiling_t,
|
|
1118
|
+
)
|
|
1119
|
+
|
|
1120
|
+
update_vrm1_expression_ui_list_elements(context)
|
|
1121
|
+
return {"FINISHED"}
|
|
1122
|
+
|
|
1123
|
+
|
|
1124
|
+
class VRM_OT_assign_vrm1_expressions_from_vrm0(Operator):
|
|
1125
|
+
bl_idname = "vrm.assign_vrm1_expressions_from_vrm0"
|
|
1126
|
+
bl_label = "Assign VRM 0.0 Blend Shapes"
|
|
1127
|
+
bl_description = "Assign VRM 1.0 Expressions from VRM 0.x Blend Shapes"
|
|
1128
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
1129
|
+
|
|
1130
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
1131
|
+
options={"HIDDEN"},
|
|
1132
|
+
)
|
|
1133
|
+
|
|
1134
|
+
def execute(self, context: Context) -> set[str]:
|
|
1135
|
+
return assign_vrm1_expressions_from_vrm0(context, self.armature_object_name)
|
|
1136
|
+
|
|
1137
|
+
if TYPE_CHECKING:
|
|
1138
|
+
# This code is auto generated.
|
|
1139
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
1140
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
1141
|
+
|
|
1142
|
+
|
|
1143
|
+
class VRM_OT_assign_vrm1_expressions_automatically(Operator):
|
|
1144
|
+
bl_idname = "vrm.assign_vrm1_expressions_automatically"
|
|
1145
|
+
bl_label = "Assign Auto-Detected Shape Keys"
|
|
1146
|
+
bl_description = "Assign VRM 1.0 expressions automatically"
|
|
1147
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
1148
|
+
|
|
1149
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
1150
|
+
options={"HIDDEN"},
|
|
1151
|
+
)
|
|
1152
|
+
|
|
1153
|
+
def execute(self, context: Context) -> set[str]:
|
|
1154
|
+
return assign_vrm1_expressions_automatically(context, self.armature_object_name)
|
|
1155
|
+
|
|
1156
|
+
if TYPE_CHECKING:
|
|
1157
|
+
# This code is auto generated.
|
|
1158
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
1159
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
1160
|
+
|
|
1161
|
+
|
|
1162
|
+
def assign_vrm1_expressions_automatically(
|
|
1163
|
+
context: Context, armature_object_name: str
|
|
1164
|
+
) -> set[str]:
|
|
1165
|
+
for mapping in (
|
|
1166
|
+
VRM1_PRESET_TO_VRCHAT_SHAPE_KEY_MAPPING,
|
|
1167
|
+
VRM1_PRESET_TO_MMD_SHAPE_KEY_MAPPING,
|
|
1168
|
+
VRM1_PRESET_TO_READY_PLAYER_ME_SHAPE_KEY_MAPPING,
|
|
1169
|
+
VRM1_PRESET_TO_ARKIT_SHAPE_KEY_MAPPING,
|
|
1170
|
+
):
|
|
1171
|
+
_add_shape_keys_to_vrm1_expressions(
|
|
1172
|
+
context,
|
|
1173
|
+
armature_object_name,
|
|
1174
|
+
mapping,
|
|
1175
|
+
)
|
|
1176
|
+
assign_vrm1_expressions_from_vrm0(context, armature_object_name)
|
|
1177
|
+
return {"FINISHED"}
|
|
1178
|
+
|
|
1179
|
+
|
|
1180
|
+
class VRM_OT_assign_vrm1_expressions_from_vrchat(Operator):
|
|
1181
|
+
bl_idname = "vrm.assign_vrm1_expressions_from_vrchat"
|
|
1182
|
+
bl_label = "Assign VRChat Shape Keys"
|
|
1183
|
+
bl_description = "Assign VRM 1.0 Expressions from VRChat Shape Keys"
|
|
1184
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
1185
|
+
|
|
1186
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
1187
|
+
options={"HIDDEN"},
|
|
1188
|
+
)
|
|
1189
|
+
|
|
1190
|
+
def execute(self, context: Context) -> set[str]:
|
|
1191
|
+
return _add_shape_keys_to_vrm1_expressions(
|
|
1192
|
+
context,
|
|
1193
|
+
self.armature_object_name,
|
|
1194
|
+
VRM1_PRESET_TO_VRCHAT_SHAPE_KEY_MAPPING,
|
|
1195
|
+
)
|
|
1196
|
+
|
|
1197
|
+
if TYPE_CHECKING:
|
|
1198
|
+
# This code is auto generated.
|
|
1199
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
1200
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
1201
|
+
|
|
1202
|
+
|
|
1203
|
+
class VRM_OT_assign_vrm1_expressions_from_mmd(Operator):
|
|
1204
|
+
bl_idname = "vrm.assign_vrm1_expressions_from_mmd"
|
|
1205
|
+
bl_label = "Assign MMD Shape Keys"
|
|
1206
|
+
bl_description = "Assign VRM 1.0 Expressions from MMD Shape Keys"
|
|
1207
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
1208
|
+
|
|
1209
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
1210
|
+
options={"HIDDEN"},
|
|
1211
|
+
)
|
|
1212
|
+
|
|
1213
|
+
def execute(self, context: Context) -> set[str]:
|
|
1214
|
+
return _add_shape_keys_to_vrm1_expressions(
|
|
1215
|
+
context,
|
|
1216
|
+
self.armature_object_name,
|
|
1217
|
+
VRM1_PRESET_TO_MMD_SHAPE_KEY_MAPPING,
|
|
1218
|
+
)
|
|
1219
|
+
|
|
1220
|
+
if TYPE_CHECKING:
|
|
1221
|
+
# This code is auto generated.
|
|
1222
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
1223
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
1224
|
+
|
|
1225
|
+
|
|
1226
|
+
class VRM_OT_assign_vrm1_expressions_from_ready_player_me(Operator):
|
|
1227
|
+
bl_idname = "vrm.assign_vrm1_expressions_from_ready_player_me"
|
|
1228
|
+
bl_label = "Assign Ready Player Me Shape Keys"
|
|
1229
|
+
bl_description = (
|
|
1230
|
+
"Assign VRM 1.0 Expressions from Ready Player Me (ARKit) Shape Keys"
|
|
1231
|
+
)
|
|
1232
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
1233
|
+
|
|
1234
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
1235
|
+
options={"HIDDEN"},
|
|
1236
|
+
)
|
|
1237
|
+
|
|
1238
|
+
def execute(self, context: Context) -> set[str]:
|
|
1239
|
+
_add_shape_keys_to_vrm1_expressions(
|
|
1240
|
+
context,
|
|
1241
|
+
self.armature_object_name,
|
|
1242
|
+
VRM1_PRESET_TO_READY_PLAYER_ME_SHAPE_KEY_MAPPING,
|
|
1243
|
+
)
|
|
1244
|
+
return _add_shape_keys_to_vrm1_expressions(
|
|
1245
|
+
context,
|
|
1246
|
+
self.armature_object_name,
|
|
1247
|
+
VRM1_PRESET_TO_ARKIT_SHAPE_KEY_MAPPING,
|
|
1248
|
+
)
|
|
1249
|
+
|
|
1250
|
+
if TYPE_CHECKING:
|
|
1251
|
+
# This code is auto generated.
|
|
1252
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
1253
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
1254
|
+
|
|
1255
|
+
|
|
1256
|
+
class VRM_OT_assign_vrm1_expressions_from_arkit(Operator):
|
|
1257
|
+
bl_idname = "vrm.assign_vrm1_expressions_from_arkit"
|
|
1258
|
+
bl_label = "Add ARkit / PerfectSync Custom Expressions"
|
|
1259
|
+
bl_description = "Assign VRM 1.0 Expressions from ARkit / PerfectSync"
|
|
1260
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
1261
|
+
|
|
1262
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
1263
|
+
options={"HIDDEN"},
|
|
1264
|
+
)
|
|
1265
|
+
|
|
1266
|
+
def execute(self, context: Context) -> set[str]:
|
|
1267
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
1268
|
+
if armature is None or armature.type != "ARMATURE":
|
|
1269
|
+
return {"CANCELLED"}
|
|
1270
|
+
armature_data = armature.data
|
|
1271
|
+
if not isinstance(armature_data, Armature):
|
|
1272
|
+
return {"CANCELLED"}
|
|
1273
|
+
|
|
1274
|
+
expressions = get_armature_extension(armature_data).vrm1.expressions
|
|
1275
|
+
|
|
1276
|
+
# Collect existing shape keys from meshes
|
|
1277
|
+
existing_shape_keys: dict[str, list[tuple[str, str]]] = {}
|
|
1278
|
+
for obj in search.export_objects(
|
|
1279
|
+
context,
|
|
1280
|
+
self.armature_object_name,
|
|
1281
|
+
export_invisibles=True,
|
|
1282
|
+
export_only_selections=False,
|
|
1283
|
+
export_lights=False,
|
|
1284
|
+
):
|
|
1285
|
+
if not isinstance(mesh_data := obj.data, Mesh):
|
|
1286
|
+
continue
|
|
1287
|
+
if not (shape_keys := mesh_data.shape_keys):
|
|
1288
|
+
continue
|
|
1289
|
+
if not (key_blocks := shape_keys.key_blocks):
|
|
1290
|
+
continue
|
|
1291
|
+
|
|
1292
|
+
for key_block in key_blocks:
|
|
1293
|
+
if key_block.name not in existing_shape_keys:
|
|
1294
|
+
existing_shape_keys[key_block.name] = []
|
|
1295
|
+
existing_shape_keys[key_block.name].append((obj.name, key_block.name))
|
|
1296
|
+
|
|
1297
|
+
# Create Custom Expression for all 52 ARKit shape keys
|
|
1298
|
+
for arkit_shape_key_name in ARKIT_SHAPE_KEYS:
|
|
1299
|
+
# Check if Expression with the same name already exists
|
|
1300
|
+
expression = None
|
|
1301
|
+
for custom_expression in expressions.custom:
|
|
1302
|
+
if custom_expression.custom_name == arkit_shape_key_name:
|
|
1303
|
+
expression = custom_expression
|
|
1304
|
+
break
|
|
1305
|
+
|
|
1306
|
+
# Create new Expression if it doesn't exist
|
|
1307
|
+
if expression is None:
|
|
1308
|
+
expression = expressions.custom.add()
|
|
1309
|
+
expression.custom_name = arkit_shape_key_name
|
|
1310
|
+
|
|
1311
|
+
# Add morph_target_binds only for shape keys that exist in meshes
|
|
1312
|
+
if arkit_shape_key_name in existing_shape_keys:
|
|
1313
|
+
for mesh_object_name, key_block_name in existing_shape_keys[
|
|
1314
|
+
arkit_shape_key_name
|
|
1315
|
+
]:
|
|
1316
|
+
# Check if the same bind already exists
|
|
1317
|
+
if any(
|
|
1318
|
+
morph_target_bind.node.mesh_object_name == mesh_object_name
|
|
1319
|
+
and morph_target_bind.index == key_block_name
|
|
1320
|
+
for morph_target_bind in expression.morph_target_binds
|
|
1321
|
+
):
|
|
1322
|
+
continue
|
|
1323
|
+
|
|
1324
|
+
# Add morph_target_bind
|
|
1325
|
+
morph_target_bind = expression.morph_target_binds.add()
|
|
1326
|
+
morph_target_bind.node.mesh_object_name = mesh_object_name
|
|
1327
|
+
morph_target_bind.index = key_block_name
|
|
1328
|
+
morph_target_bind.weight = 1.0
|
|
1329
|
+
|
|
1330
|
+
_add_shape_keys_to_vrm1_expressions(
|
|
1331
|
+
context,
|
|
1332
|
+
self.armature_object_name,
|
|
1333
|
+
VRM1_PRESET_TO_ARKIT_SHAPE_KEY_MAPPING,
|
|
1334
|
+
)
|
|
1335
|
+
update_vrm1_expression_ui_list_elements(context)
|
|
1336
|
+
return {"FINISHED"}
|
|
1337
|
+
|
|
1338
|
+
if TYPE_CHECKING:
|
|
1339
|
+
# This code is auto generated.
|
|
1340
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
1341
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
1342
|
+
|
|
1343
|
+
|
|
1344
|
+
class VRM_OT_add_vrm1_expression_material_color_bind(Operator):
|
|
1345
|
+
bl_idname = "vrm.add_vrm1_expression_material_color_bind"
|
|
1346
|
+
bl_label = "Add Material Color Bind"
|
|
1347
|
+
bl_description = "Add VRM 1.0 Expression Material Value Bind"
|
|
1348
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
1349
|
+
|
|
1350
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
1351
|
+
options={"HIDDEN"},
|
|
1352
|
+
)
|
|
1353
|
+
|
|
1354
|
+
expression_name: StringProperty( # type: ignore[valid-type]
|
|
1355
|
+
options={"HIDDEN"},
|
|
1356
|
+
)
|
|
1357
|
+
|
|
1358
|
+
def execute(self, context: Context) -> set[str]:
|
|
1359
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
1360
|
+
if armature is None or armature.type != "ARMATURE":
|
|
1361
|
+
return {"CANCELLED"}
|
|
1362
|
+
armature_data = armature.data
|
|
1363
|
+
if not isinstance(armature_data, Armature):
|
|
1364
|
+
return {"CANCELLED"}
|
|
1365
|
+
ext = get_armature_extension(armature_data)
|
|
1366
|
+
expression = ext.vrm1.expressions.all_name_to_expression_dict().get(
|
|
1367
|
+
self.expression_name
|
|
1368
|
+
)
|
|
1369
|
+
if expression is None:
|
|
1370
|
+
return {"CANCELLED"}
|
|
1371
|
+
expression.material_color_binds.add()
|
|
1372
|
+
expression.active_material_color_bind_index = (
|
|
1373
|
+
len(expression.material_color_binds) - 1
|
|
1374
|
+
)
|
|
1375
|
+
return {"FINISHED"}
|
|
1376
|
+
|
|
1377
|
+
if TYPE_CHECKING:
|
|
1378
|
+
# This code is auto generated.
|
|
1379
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
1380
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
1381
|
+
expression_name: str # type: ignore[no-redef]
|
|
1382
|
+
|
|
1383
|
+
|
|
1384
|
+
class VRM_OT_remove_vrm1_expression_material_color_bind(Operator):
|
|
1385
|
+
bl_idname = "vrm.remove_vrm1_expression_material_color_bind"
|
|
1386
|
+
bl_label = "Remove Material Color Bind"
|
|
1387
|
+
bl_description = "Remove VRM 1.0 Expression Material Color Bind"
|
|
1388
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
1389
|
+
|
|
1390
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
1391
|
+
options={"HIDDEN"},
|
|
1392
|
+
)
|
|
1393
|
+
|
|
1394
|
+
expression_name: StringProperty( # type: ignore[valid-type]
|
|
1395
|
+
options={"HIDDEN"},
|
|
1396
|
+
)
|
|
1397
|
+
|
|
1398
|
+
bind_index: IntProperty( # type: ignore[valid-type]
|
|
1399
|
+
options={"HIDDEN"},
|
|
1400
|
+
min=0,
|
|
1401
|
+
)
|
|
1402
|
+
|
|
1403
|
+
def execute(self, context: Context) -> set[str]:
|
|
1404
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
1405
|
+
if armature is None or armature.type != "ARMATURE":
|
|
1406
|
+
return {"CANCELLED"}
|
|
1407
|
+
armature_data = armature.data
|
|
1408
|
+
if not isinstance(armature_data, Armature):
|
|
1409
|
+
return {"CANCELLED"}
|
|
1410
|
+
expressions = get_armature_extension(armature_data).vrm1.expressions
|
|
1411
|
+
expression = expressions.all_name_to_expression_dict().get(self.expression_name)
|
|
1412
|
+
if expression is None:
|
|
1413
|
+
return {"CANCELLED"}
|
|
1414
|
+
if len(expression.material_color_binds) <= self.bind_index:
|
|
1415
|
+
return {"CANCELLED"}
|
|
1416
|
+
expression.material_color_binds.remove(self.bind_index)
|
|
1417
|
+
expression.active_material_color_bind_index = min(
|
|
1418
|
+
expression.active_material_color_bind_index,
|
|
1419
|
+
max(0, len(expression.material_color_binds) - 1),
|
|
1420
|
+
)
|
|
1421
|
+
return {"FINISHED"}
|
|
1422
|
+
|
|
1423
|
+
if TYPE_CHECKING:
|
|
1424
|
+
# This code is auto generated.
|
|
1425
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
1426
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
1427
|
+
expression_name: str # type: ignore[no-redef]
|
|
1428
|
+
bind_index: int # type: ignore[no-redef]
|
|
1429
|
+
|
|
1430
|
+
|
|
1431
|
+
class VRM_OT_move_up_vrm1_expression_material_color_bind(Operator):
|
|
1432
|
+
bl_idname = "vrm.move_up_vrm1_expression_material_color_bind"
|
|
1433
|
+
bl_label = "Move Up Material Color Bind"
|
|
1434
|
+
bl_description = "Move Up VRM 1.0 Expression Material Color Bind"
|
|
1435
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
1436
|
+
|
|
1437
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
1438
|
+
options={"HIDDEN"},
|
|
1439
|
+
)
|
|
1440
|
+
|
|
1441
|
+
expression_name: StringProperty( # type: ignore[valid-type]
|
|
1442
|
+
options={"HIDDEN"},
|
|
1443
|
+
)
|
|
1444
|
+
|
|
1445
|
+
bind_index: IntProperty( # type: ignore[valid-type]
|
|
1446
|
+
options={"HIDDEN"},
|
|
1447
|
+
min=0,
|
|
1448
|
+
)
|
|
1449
|
+
|
|
1450
|
+
def execute(self, context: Context) -> set[str]:
|
|
1451
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
1452
|
+
if armature is None or armature.type != "ARMATURE":
|
|
1453
|
+
return {"CANCELLED"}
|
|
1454
|
+
armature_data = armature.data
|
|
1455
|
+
if not isinstance(armature_data, Armature):
|
|
1456
|
+
return {"CANCELLED"}
|
|
1457
|
+
expressions = get_armature_extension(armature_data).vrm1.expressions
|
|
1458
|
+
expression = expressions.all_name_to_expression_dict().get(self.expression_name)
|
|
1459
|
+
if expression is None:
|
|
1460
|
+
return {"CANCELLED"}
|
|
1461
|
+
if len(expression.material_color_binds) <= self.bind_index:
|
|
1462
|
+
return {"CANCELLED"}
|
|
1463
|
+
new_bind_index = (self.bind_index - 1) % len(expression.material_color_binds)
|
|
1464
|
+
expression.material_color_binds.move(self.bind_index, new_bind_index)
|
|
1465
|
+
expression.active_material_color_bind_index = new_bind_index
|
|
1466
|
+
return {"FINISHED"}
|
|
1467
|
+
|
|
1468
|
+
if TYPE_CHECKING:
|
|
1469
|
+
# This code is auto generated.
|
|
1470
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
1471
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
1472
|
+
expression_name: str # type: ignore[no-redef]
|
|
1473
|
+
bind_index: int # type: ignore[no-redef]
|
|
1474
|
+
|
|
1475
|
+
|
|
1476
|
+
class VRM_OT_move_down_vrm1_expression_material_color_bind(Operator):
|
|
1477
|
+
bl_idname = "vrm.move_down_vrm1_expression_material_color_bind"
|
|
1478
|
+
bl_label = "Move Down Material Color Bind"
|
|
1479
|
+
bl_description = "Move Down VRM 1.0 Expression Material Color Bind"
|
|
1480
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
1481
|
+
|
|
1482
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
1483
|
+
options={"HIDDEN"},
|
|
1484
|
+
)
|
|
1485
|
+
|
|
1486
|
+
expression_name: StringProperty( # type: ignore[valid-type]
|
|
1487
|
+
options={"HIDDEN"},
|
|
1488
|
+
)
|
|
1489
|
+
|
|
1490
|
+
bind_index: IntProperty( # type: ignore[valid-type]
|
|
1491
|
+
options={"HIDDEN"},
|
|
1492
|
+
min=0,
|
|
1493
|
+
)
|
|
1494
|
+
|
|
1495
|
+
def execute(self, context: Context) -> set[str]:
|
|
1496
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
1497
|
+
if armature is None or armature.type != "ARMATURE":
|
|
1498
|
+
return {"CANCELLED"}
|
|
1499
|
+
armature_data = armature.data
|
|
1500
|
+
if not isinstance(armature_data, Armature):
|
|
1501
|
+
return {"CANCELLED"}
|
|
1502
|
+
expressions = get_armature_extension(armature_data).vrm1.expressions
|
|
1503
|
+
expression = expressions.all_name_to_expression_dict().get(self.expression_name)
|
|
1504
|
+
if expression is None:
|
|
1505
|
+
return {"CANCELLED"}
|
|
1506
|
+
if len(expression.material_color_binds) <= self.bind_index:
|
|
1507
|
+
return {"CANCELLED"}
|
|
1508
|
+
new_bind_index = (self.bind_index + 1) % len(expression.material_color_binds)
|
|
1509
|
+
expression.material_color_binds.move(self.bind_index, new_bind_index)
|
|
1510
|
+
expression.active_material_color_bind_index = new_bind_index
|
|
1511
|
+
return {"FINISHED"}
|
|
1512
|
+
|
|
1513
|
+
if TYPE_CHECKING:
|
|
1514
|
+
# This code is auto generated.
|
|
1515
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
1516
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
1517
|
+
expression_name: str # type: ignore[no-redef]
|
|
1518
|
+
bind_index: int # type: ignore[no-redef]
|
|
1519
|
+
|
|
1520
|
+
|
|
1521
|
+
class VRM_OT_add_vrm1_expression_texture_transform_bind(Operator):
|
|
1522
|
+
bl_idname = "vrm.add_vrm1_expression_texture_transform_bind"
|
|
1523
|
+
bl_label = "Add Texture Transform Bind"
|
|
1524
|
+
bl_description = "Add VRM 1.0 Expression Texture Transform Bind"
|
|
1525
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
1526
|
+
|
|
1527
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
1528
|
+
options={"HIDDEN"},
|
|
1529
|
+
)
|
|
1530
|
+
|
|
1531
|
+
expression_name: StringProperty( # type: ignore[valid-type]
|
|
1532
|
+
options={"HIDDEN"},
|
|
1533
|
+
)
|
|
1534
|
+
|
|
1535
|
+
def execute(self, context: Context) -> set[str]:
|
|
1536
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
1537
|
+
if armature is None or armature.type != "ARMATURE":
|
|
1538
|
+
return {"CANCELLED"}
|
|
1539
|
+
armature_data = armature.data
|
|
1540
|
+
if not isinstance(armature_data, Armature):
|
|
1541
|
+
return {"CANCELLED"}
|
|
1542
|
+
expressions = get_armature_extension(armature_data).vrm1.expressions
|
|
1543
|
+
expression = expressions.all_name_to_expression_dict().get(self.expression_name)
|
|
1544
|
+
if expression is None:
|
|
1545
|
+
return {"CANCELLED"}
|
|
1546
|
+
expression.texture_transform_binds.add()
|
|
1547
|
+
expression.active_texture_transform_bind_index = (
|
|
1548
|
+
len(expression.texture_transform_binds) - 1
|
|
1549
|
+
)
|
|
1550
|
+
return {"FINISHED"}
|
|
1551
|
+
|
|
1552
|
+
if TYPE_CHECKING:
|
|
1553
|
+
# This code is auto generated.
|
|
1554
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
1555
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
1556
|
+
expression_name: str # type: ignore[no-redef]
|
|
1557
|
+
|
|
1558
|
+
|
|
1559
|
+
class VRM_OT_remove_vrm1_expression_texture_transform_bind(Operator):
|
|
1560
|
+
bl_idname = "vrm.remove_vrm1_expression_texture_transform_bind"
|
|
1561
|
+
bl_label = "Remove Texture Transform Bind"
|
|
1562
|
+
bl_description = "Remove VRM 1.0 Expression Texture Transform Bind"
|
|
1563
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
1564
|
+
|
|
1565
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
1566
|
+
options={"HIDDEN"},
|
|
1567
|
+
)
|
|
1568
|
+
|
|
1569
|
+
expression_name: StringProperty( # type: ignore[valid-type]
|
|
1570
|
+
options={"HIDDEN"},
|
|
1571
|
+
)
|
|
1572
|
+
|
|
1573
|
+
bind_index: IntProperty( # type: ignore[valid-type]
|
|
1574
|
+
options={"HIDDEN"},
|
|
1575
|
+
min=0,
|
|
1576
|
+
)
|
|
1577
|
+
|
|
1578
|
+
def execute(self, context: Context) -> set[str]:
|
|
1579
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
1580
|
+
if armature is None or armature.type != "ARMATURE":
|
|
1581
|
+
return {"CANCELLED"}
|
|
1582
|
+
armature_data = armature.data
|
|
1583
|
+
if not isinstance(armature_data, Armature):
|
|
1584
|
+
return {"CANCELLED"}
|
|
1585
|
+
expressions = get_armature_extension(armature_data).vrm1.expressions
|
|
1586
|
+
expression = expressions.all_name_to_expression_dict().get(self.expression_name)
|
|
1587
|
+
if expression is None:
|
|
1588
|
+
return {"CANCELLED"}
|
|
1589
|
+
if len(expression.texture_transform_binds) <= self.bind_index:
|
|
1590
|
+
return {"CANCELLED"}
|
|
1591
|
+
expression.texture_transform_binds.remove(self.bind_index)
|
|
1592
|
+
expression.active_texture_transform_bind_index = min(
|
|
1593
|
+
expression.active_texture_transform_bind_index,
|
|
1594
|
+
max(0, len(expression.texture_transform_binds) - 1),
|
|
1595
|
+
)
|
|
1596
|
+
reset_expression_material_binds(context)
|
|
1597
|
+
return {"FINISHED"}
|
|
1598
|
+
|
|
1599
|
+
if TYPE_CHECKING:
|
|
1600
|
+
# This code is auto generated.
|
|
1601
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
1602
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
1603
|
+
expression_name: str # type: ignore[no-redef]
|
|
1604
|
+
bind_index: int # type: ignore[no-redef]
|
|
1605
|
+
|
|
1606
|
+
|
|
1607
|
+
class VRM_OT_move_up_vrm1_expression_texture_transform_bind(Operator):
|
|
1608
|
+
bl_idname = "vrm.move_up_vrm1_expression_texture_transform_bind"
|
|
1609
|
+
bl_label = "Move Up Texture Transform Bind"
|
|
1610
|
+
bl_description = "Move Up VRM 1.0 Expression Texture Transform Bind"
|
|
1611
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
1612
|
+
|
|
1613
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
1614
|
+
options={"HIDDEN"},
|
|
1615
|
+
)
|
|
1616
|
+
|
|
1617
|
+
expression_name: StringProperty( # type: ignore[valid-type]
|
|
1618
|
+
options={"HIDDEN"},
|
|
1619
|
+
)
|
|
1620
|
+
|
|
1621
|
+
bind_index: IntProperty( # type: ignore[valid-type]
|
|
1622
|
+
options={"HIDDEN"},
|
|
1623
|
+
min=0,
|
|
1624
|
+
)
|
|
1625
|
+
|
|
1626
|
+
def execute(self, context: Context) -> set[str]:
|
|
1627
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
1628
|
+
if armature is None or armature.type != "ARMATURE":
|
|
1629
|
+
return {"CANCELLED"}
|
|
1630
|
+
armature_data = armature.data
|
|
1631
|
+
if not isinstance(armature_data, Armature):
|
|
1632
|
+
return {"CANCELLED"}
|
|
1633
|
+
expressions = get_armature_extension(armature_data).vrm1.expressions
|
|
1634
|
+
expression = expressions.all_name_to_expression_dict().get(self.expression_name)
|
|
1635
|
+
if expression is None:
|
|
1636
|
+
return {"CANCELLED"}
|
|
1637
|
+
if len(expression.texture_transform_binds) <= self.bind_index:
|
|
1638
|
+
return {"CANCELLED"}
|
|
1639
|
+
new_bind_index = (self.bind_index - 1) % len(expression.texture_transform_binds)
|
|
1640
|
+
expression.texture_transform_binds.move(self.bind_index, new_bind_index)
|
|
1641
|
+
expression.active_texture_transform_bind_index = new_bind_index
|
|
1642
|
+
return {"FINISHED"}
|
|
1643
|
+
|
|
1644
|
+
if TYPE_CHECKING:
|
|
1645
|
+
# This code is auto generated.
|
|
1646
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
1647
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
1648
|
+
expression_name: str # type: ignore[no-redef]
|
|
1649
|
+
bind_index: int # type: ignore[no-redef]
|
|
1650
|
+
|
|
1651
|
+
|
|
1652
|
+
class VRM_OT_move_down_vrm1_expression_texture_transform_bind(Operator):
|
|
1653
|
+
bl_idname = "vrm.move_down_vrm1_expression_texture_transform_bind"
|
|
1654
|
+
bl_label = "Move Down Morph Target Bind"
|
|
1655
|
+
bl_description = "Move Down VRM 1.0 Expression Morph Target Bind"
|
|
1656
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
1657
|
+
|
|
1658
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
1659
|
+
options={"HIDDEN"},
|
|
1660
|
+
)
|
|
1661
|
+
|
|
1662
|
+
expression_name: StringProperty( # type: ignore[valid-type]
|
|
1663
|
+
options={"HIDDEN"},
|
|
1664
|
+
)
|
|
1665
|
+
|
|
1666
|
+
bind_index: IntProperty( # type: ignore[valid-type]
|
|
1667
|
+
options={"HIDDEN"},
|
|
1668
|
+
min=0,
|
|
1669
|
+
)
|
|
1670
|
+
|
|
1671
|
+
def execute(self, context: Context) -> set[str]:
|
|
1672
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
1673
|
+
if armature is None or armature.type != "ARMATURE":
|
|
1674
|
+
return {"CANCELLED"}
|
|
1675
|
+
armature_data = armature.data
|
|
1676
|
+
if not isinstance(armature_data, Armature):
|
|
1677
|
+
return {"CANCELLED"}
|
|
1678
|
+
expressions = get_armature_extension(armature_data).vrm1.expressions
|
|
1679
|
+
expression = expressions.all_name_to_expression_dict().get(self.expression_name)
|
|
1680
|
+
if expression is None:
|
|
1681
|
+
return {"CANCELLED"}
|
|
1682
|
+
if len(expression.texture_transform_binds) <= self.bind_index:
|
|
1683
|
+
return {"CANCELLED"}
|
|
1684
|
+
new_bind_index = (self.bind_index + 1) % len(expression.texture_transform_binds)
|
|
1685
|
+
expression.texture_transform_binds.move(self.bind_index, new_bind_index)
|
|
1686
|
+
expression.active_texture_transform_bind_index = new_bind_index
|
|
1687
|
+
return {"FINISHED"}
|
|
1688
|
+
|
|
1689
|
+
if TYPE_CHECKING:
|
|
1690
|
+
# This code is auto generated.
|
|
1691
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
1692
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
1693
|
+
expression_name: str # type: ignore[no-redef]
|
|
1694
|
+
bind_index: int # type: ignore[no-redef]
|
|
1695
|
+
|
|
1696
|
+
|
|
1697
|
+
class VRM_OT_assign_vrm1_humanoid_human_bones_automatically(Operator):
|
|
1698
|
+
bl_idname = "vrm.assign_vrm1_humanoid_human_bones_automatically"
|
|
1699
|
+
bl_label = "Automatic Bone Assignment"
|
|
1700
|
+
bl_description = "Assign VRM 1.0 Humanoid Human Bones"
|
|
1701
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
1702
|
+
|
|
1703
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
1704
|
+
options={"HIDDEN"},
|
|
1705
|
+
)
|
|
1706
|
+
|
|
1707
|
+
def execute(self, context: Context) -> set[str]:
|
|
1708
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
1709
|
+
if armature is None:
|
|
1710
|
+
return {"CANCELLED"}
|
|
1711
|
+
return assign_vrm1_humanoid_human_bones_automatically(context, armature)
|
|
1712
|
+
|
|
1713
|
+
if TYPE_CHECKING:
|
|
1714
|
+
# This code is auto generated.
|
|
1715
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
1716
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
1717
|
+
|
|
1718
|
+
|
|
1719
|
+
def assign_vrm1_humanoid_human_bones_automatically(
|
|
1720
|
+
context: Context, armature: Object
|
|
1721
|
+
) -> set[str]:
|
|
1722
|
+
if armature.type != "ARMATURE":
|
|
1723
|
+
return {"CANCELLED"}
|
|
1724
|
+
armature_data = armature.data
|
|
1725
|
+
if not isinstance(armature_data, Armature):
|
|
1726
|
+
return {"CANCELLED"}
|
|
1727
|
+
Vrm1HumanBonesPropertyGroup.fixup_human_bones(armature)
|
|
1728
|
+
Vrm1HumanBonesPropertyGroup.update_all_bone_name_candidates(
|
|
1729
|
+
context, armature_data.name
|
|
1730
|
+
)
|
|
1731
|
+
human_bones = get_armature_extension(armature_data).vrm1.humanoid.human_bones
|
|
1732
|
+
human_bone_name_to_human_bone = human_bones.human_bone_name_to_human_bone()
|
|
1733
|
+
bones = armature_data.bones
|
|
1734
|
+
|
|
1735
|
+
Vrm0HumanoidPropertyGroup.fixup_human_bones(armature)
|
|
1736
|
+
Vrm0HumanoidPropertyGroup.update_all_bone_name_candidates(
|
|
1737
|
+
context, armature_data.name
|
|
1738
|
+
)
|
|
1739
|
+
vrm0_human_bone_name_to_vrm1_human_bone_name = {
|
|
1740
|
+
specification.vrm0_name: specification.name
|
|
1741
|
+
for specification in HumanBoneSpecifications.all_human_bones
|
|
1742
|
+
}
|
|
1743
|
+
vrm0_humanoid = get_armature_extension(armature_data).vrm0.humanoid
|
|
1744
|
+
if vrm0_humanoid.bones_are_correctly_assigned():
|
|
1745
|
+
for vrm0_human_bone in vrm0_humanoid.human_bones:
|
|
1746
|
+
if (
|
|
1747
|
+
vrm0_human_bone.node.bone_name
|
|
1748
|
+
not in vrm0_human_bone.node.bone_name_candidates
|
|
1749
|
+
):
|
|
1750
|
+
continue
|
|
1751
|
+
vrm0_name = Vrm0HumanBoneName.from_str(vrm0_human_bone.bone)
|
|
1752
|
+
if not vrm0_name:
|
|
1753
|
+
logger.error("Invalid VRM0 bone name str: %s", vrm0_human_bone.bone)
|
|
1754
|
+
continue
|
|
1755
|
+
vrm1_name = vrm0_human_bone_name_to_vrm1_human_bone_name.get(vrm0_name)
|
|
1756
|
+
if vrm1_name is None:
|
|
1757
|
+
logger.error("Invalid VRM0 bone name: %s", vrm0_name)
|
|
1758
|
+
continue
|
|
1759
|
+
human_bone = human_bone_name_to_human_bone.get(vrm1_name)
|
|
1760
|
+
if not human_bone:
|
|
1761
|
+
continue
|
|
1762
|
+
if (
|
|
1763
|
+
vrm0_human_bone.node.bone_name
|
|
1764
|
+
not in human_bone.node.bone_name_candidates
|
|
1765
|
+
):
|
|
1766
|
+
continue
|
|
1767
|
+
human_bone.node.bone_name = vrm0_human_bone.node.bone_name
|
|
1768
|
+
|
|
1769
|
+
default_mapping = {
|
|
1770
|
+
bone_name: HumanBoneSpecifications.get(human_bone_name)
|
|
1771
|
+
for human_bone_name, human_bone in human_bone_name_to_human_bone.items()
|
|
1772
|
+
if (bone_name := human_bone.node.bone_name)
|
|
1773
|
+
}
|
|
1774
|
+
default_requied_bone_count = sum(
|
|
1775
|
+
1
|
|
1776
|
+
for human_bone_specification in default_mapping.values()
|
|
1777
|
+
if human_bone_specification.requirement
|
|
1778
|
+
)
|
|
1779
|
+
|
|
1780
|
+
generated_mapping = create_human_bone_mapping(armature)
|
|
1781
|
+
generated_required_bone_count = sum(
|
|
1782
|
+
1
|
|
1783
|
+
for human_bone_specification in generated_mapping.values()
|
|
1784
|
+
if human_bone_specification.requirement
|
|
1785
|
+
)
|
|
1786
|
+
|
|
1787
|
+
if default_requied_bone_count > generated_required_bone_count:
|
|
1788
|
+
return {"CANCELLED"}
|
|
1789
|
+
|
|
1790
|
+
for human_bone in human_bone_name_to_human_bone.values():
|
|
1791
|
+
human_bone.node.bone_name = ""
|
|
1792
|
+
|
|
1793
|
+
for (
|
|
1794
|
+
bone_name,
|
|
1795
|
+
specification,
|
|
1796
|
+
) in generated_mapping.items():
|
|
1797
|
+
bone = bones.get(bone_name)
|
|
1798
|
+
if not bone:
|
|
1799
|
+
continue
|
|
1800
|
+
|
|
1801
|
+
for search_name, human_bone in human_bone_name_to_human_bone.items():
|
|
1802
|
+
if (
|
|
1803
|
+
specification.name != search_name
|
|
1804
|
+
or human_bone.node.bone_name in human_bone.node.bone_name_candidates
|
|
1805
|
+
or bone_name not in human_bone.node.bone_name_candidates
|
|
1806
|
+
):
|
|
1807
|
+
continue
|
|
1808
|
+
human_bone.node.bone_name = bone_name
|
|
1809
|
+
break
|
|
1810
|
+
|
|
1811
|
+
Vrm1HumanBonesPropertyGroup.update_all_bone_name_candidates(
|
|
1812
|
+
context, armature_data.name, force=True
|
|
1813
|
+
)
|
|
1814
|
+
return {"FINISHED"}
|
|
1815
|
+
|
|
1816
|
+
|
|
1817
|
+
class VRM_OT_update_vrm1_expression_ui_list_elements(Operator):
|
|
1818
|
+
bl_idname = "vrm.update_vrm1_expression_ui_list_elements"
|
|
1819
|
+
bl_label = "Update VRM 1.0 Expression UI List Elements"
|
|
1820
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
1821
|
+
|
|
1822
|
+
def execute(self, context: Context) -> set[str]:
|
|
1823
|
+
update_vrm1_expression_ui_list_elements(context)
|
|
1824
|
+
return {"FINISHED"}
|
|
1825
|
+
|
|
1826
|
+
|
|
1827
|
+
def update_vrm1_expression_ui_list_elements(context: Context) -> None:
|
|
1828
|
+
for armature in context.blend_data.armatures:
|
|
1829
|
+
expressions = get_armature_extension(armature).vrm1.expressions
|
|
1830
|
+
|
|
1831
|
+
# Set the number of elements equal to the number of elements wanted to show
|
|
1832
|
+
# in the UIList.
|
|
1833
|
+
ui_len = len(expressions.expression_ui_list_elements)
|
|
1834
|
+
all_len = len(expressions.all_name_to_expression_dict())
|
|
1835
|
+
if ui_len == all_len:
|
|
1836
|
+
continue
|
|
1837
|
+
if ui_len > all_len:
|
|
1838
|
+
for _ in range(ui_len - all_len):
|
|
1839
|
+
expressions.expression_ui_list_elements.remove(0)
|
|
1840
|
+
if all_len > ui_len:
|
|
1841
|
+
for _ in range(all_len - ui_len):
|
|
1842
|
+
expressions.expression_ui_list_elements.add()
|
|
1843
|
+
|
|
1844
|
+
|
|
1845
|
+
class TextureTransformBind(Protocol):
|
|
1846
|
+
material: Material
|
|
1847
|
+
scale: tuple[float, float]
|
|
1848
|
+
offset: tuple[float, float]
|
|
1849
|
+
|
|
1850
|
+
|
|
1851
|
+
class VRM_OT_refresh_vrm1_expression_texture_transform_bind_preview(Operator):
|
|
1852
|
+
bl_idname = "vrm.refresh_vrm1_expression_texture_transform_bind_preview"
|
|
1853
|
+
bl_label = "Refresh Texture Transform Bind Preview"
|
|
1854
|
+
bl_description = "Refresh VRM 1.0 Expression Texture Transform Bind Preview"
|
|
1855
|
+
bl_options: ClassVar = {"REGISTER", "UNDO"}
|
|
1856
|
+
|
|
1857
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
1858
|
+
options={"HIDDEN"},
|
|
1859
|
+
)
|
|
1860
|
+
|
|
1861
|
+
expression_name: StringProperty( # type: ignore[valid-type]
|
|
1862
|
+
options={"HIDDEN"}
|
|
1863
|
+
)
|
|
1864
|
+
|
|
1865
|
+
_preset_name_mapping: Mapping[str, str] = {
|
|
1866
|
+
"neutral": "neutral",
|
|
1867
|
+
"aa": "aa",
|
|
1868
|
+
"ih": "ih",
|
|
1869
|
+
"ou": "ou",
|
|
1870
|
+
"ee": "ee",
|
|
1871
|
+
"oh": "oh",
|
|
1872
|
+
"blink": "blink",
|
|
1873
|
+
"joy": "happy",
|
|
1874
|
+
"angry": "angry",
|
|
1875
|
+
"sorrow": "sad",
|
|
1876
|
+
"fun": "surprised",
|
|
1877
|
+
"relaxed": "relaxed",
|
|
1878
|
+
"blinkLeft": "blink_left",
|
|
1879
|
+
"blinkRight": "blink_right",
|
|
1880
|
+
"lookUp": "look_up",
|
|
1881
|
+
"lookDown": "look_down",
|
|
1882
|
+
"lookLeft": "look_left",
|
|
1883
|
+
"lookRight": "look_right",
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
def execute(self, context: Context) -> set[str]:
|
|
1887
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
1888
|
+
if armature is None or armature.type != "ARMATURE":
|
|
1889
|
+
return {"CANCELLED"}
|
|
1890
|
+
armature_data = armature.data
|
|
1891
|
+
if not isinstance(armature_data, bpy.types.Armature):
|
|
1892
|
+
return {"CANCELLED"}
|
|
1893
|
+
all_expressions = self.get_all_expressions()
|
|
1894
|
+
|
|
1895
|
+
materials_to_update: set[Material] = set()
|
|
1896
|
+
expression = None
|
|
1897
|
+
for expression, _expr_type, _expr_name in all_expressions:
|
|
1898
|
+
for bind in expression.texture_transform_binds:
|
|
1899
|
+
if bind.material:
|
|
1900
|
+
materials_to_update.add(bind.material)
|
|
1901
|
+
if not expression:
|
|
1902
|
+
return {"FINISHED"}
|
|
1903
|
+
|
|
1904
|
+
for material in materials_to_update:
|
|
1905
|
+
self.setup_uv_offset_nodes(context, armature, material, all_expressions)
|
|
1906
|
+
new_item = expression.materials_to_update.add()
|
|
1907
|
+
new_item.material = material
|
|
1908
|
+
material.update_tag()
|
|
1909
|
+
|
|
1910
|
+
return {"FINISHED"}
|
|
1911
|
+
|
|
1912
|
+
def get_all_expressions(self) -> list[tuple[Vrm1ExpressionPropertyGroup, str, str]]:
|
|
1913
|
+
armature = bpy.data.objects.get(self.armature_object_name)
|
|
1914
|
+
if not armature or armature.type != "ARMATURE":
|
|
1915
|
+
return []
|
|
1916
|
+
armature_data = armature.data
|
|
1917
|
+
if not isinstance(armature_data, Armature):
|
|
1918
|
+
return []
|
|
1919
|
+
ext = get_armature_extension(armature_data)
|
|
1920
|
+
expressions = ext.vrm1.expressions
|
|
1921
|
+
|
|
1922
|
+
all_expressions: list[tuple[Vrm1ExpressionPropertyGroup, str, str]] = []
|
|
1923
|
+
for preset_name in self._preset_name_mapping.values():
|
|
1924
|
+
preset_expr = getattr(expressions.preset, preset_name, None)
|
|
1925
|
+
if preset_expr:
|
|
1926
|
+
all_expressions.append((preset_expr, "preset", preset_name))
|
|
1927
|
+
for i, custom_expr in enumerate(expressions.custom):
|
|
1928
|
+
all_expressions.append((custom_expr, "custom", str(i)))
|
|
1929
|
+
|
|
1930
|
+
return all_expressions
|
|
1931
|
+
|
|
1932
|
+
def setup_uv_offset_nodes(
|
|
1933
|
+
self,
|
|
1934
|
+
context: Context,
|
|
1935
|
+
armature: Object,
|
|
1936
|
+
material: Material,
|
|
1937
|
+
all_expressions: list[tuple[Vrm1ExpressionPropertyGroup, str, str]],
|
|
1938
|
+
) -> None:
|
|
1939
|
+
if bpy.app.version < (5, 0, 0) and not material.use_nodes:
|
|
1940
|
+
material.use_nodes = True
|
|
1941
|
+
|
|
1942
|
+
node_tree = material.node_tree
|
|
1943
|
+
if not isinstance(node_tree, bpy.types.ShaderNodeTree):
|
|
1944
|
+
return
|
|
1945
|
+
|
|
1946
|
+
self.remove_existing_nodes(node_tree)
|
|
1947
|
+
|
|
1948
|
+
# Create a new node group
|
|
1949
|
+
group_name = f"VRM_TextureTransform_{material.name}"
|
|
1950
|
+
node_group = bpy.data.node_groups.new(type="ShaderNodeTree", name=group_name)
|
|
1951
|
+
|
|
1952
|
+
# Create input and output sockets for the node group
|
|
1953
|
+
node_group.interface.new_socket(
|
|
1954
|
+
name="UV", in_out="INPUT", socket_type="NodeSocketVector"
|
|
1955
|
+
)
|
|
1956
|
+
node_group.interface.new_socket(
|
|
1957
|
+
name="Vector", in_out="OUTPUT", socket_type="NodeSocketVector"
|
|
1958
|
+
)
|
|
1959
|
+
|
|
1960
|
+
# Add nodes to the group
|
|
1961
|
+
_group_input = node_group.nodes.new("NodeGroupInput")
|
|
1962
|
+
group_output = node_group.nodes.new("NodeGroupOutput")
|
|
1963
|
+
|
|
1964
|
+
# Create a single UV Map node
|
|
1965
|
+
uv_map_node = node_group.nodes.new(type="ShaderNodeUVMap")
|
|
1966
|
+
uv_map_node.name = "VRM_TextureTransform_UVMap"
|
|
1967
|
+
|
|
1968
|
+
# Setup drivers and create mapping nodes
|
|
1969
|
+
mapping_nodes = self.setup_drivers(
|
|
1970
|
+
context, armature, node_group, material, all_expressions
|
|
1971
|
+
)
|
|
1972
|
+
|
|
1973
|
+
# Link the UV Map node to all mapping nodes
|
|
1974
|
+
for mapping_node in mapping_nodes.values():
|
|
1975
|
+
node_group.links.new(uv_map_node.outputs[0], mapping_node.inputs[0])
|
|
1976
|
+
|
|
1977
|
+
# Create blocking multiply chains
|
|
1978
|
+
multiply_chains = self.create_blocking_multiply_chains(
|
|
1979
|
+
node_group, all_expressions, armature, mapping_nodes
|
|
1980
|
+
)
|
|
1981
|
+
|
|
1982
|
+
# Create the final add chain
|
|
1983
|
+
final_add_node = self.create_final_add_chain(
|
|
1984
|
+
node_group, mapping_nodes, multiply_chains, all_expressions
|
|
1985
|
+
)
|
|
1986
|
+
|
|
1987
|
+
# Connect the final add node to the group output
|
|
1988
|
+
if final_add_node:
|
|
1989
|
+
node_group.links.new(final_add_node.outputs[0], group_output.inputs[0])
|
|
1990
|
+
|
|
1991
|
+
# Add the group node to the material
|
|
1992
|
+
group_node = node_tree.nodes.new(type="ShaderNodeGroup")
|
|
1993
|
+
if not isinstance(group_node, ShaderNodeGroup):
|
|
1994
|
+
raise TypeError
|
|
1995
|
+
group_node.node_tree = node_group
|
|
1996
|
+
group_node.name = "VRM_TextureTransform_Group"
|
|
1997
|
+
|
|
1998
|
+
# Connect the group node to texture nodes
|
|
1999
|
+
for node in node_tree.nodes:
|
|
2000
|
+
if node.type == "TEX_IMAGE":
|
|
2001
|
+
if not isinstance(node, ShaderNodeTexImage):
|
|
2002
|
+
raise TypeError
|
|
2003
|
+
self.connect_group_to_image_node(node_tree, group_node, node)
|
|
2004
|
+
|
|
2005
|
+
def setup_drivers(
|
|
2006
|
+
self,
|
|
2007
|
+
_context: Context,
|
|
2008
|
+
armature: Object,
|
|
2009
|
+
node_group: NodeTree,
|
|
2010
|
+
material: Material,
|
|
2011
|
+
all_expressions: Sequence[tuple[Vrm1ExpressionPropertyGroup, str, str]],
|
|
2012
|
+
) -> Mapping[str, ShaderNodeMapping]:
|
|
2013
|
+
mapping_nodes = self.create_mapping_nodes(
|
|
2014
|
+
node_group, all_expressions, armature, material
|
|
2015
|
+
)
|
|
2016
|
+
return mapping_nodes
|
|
2017
|
+
|
|
2018
|
+
def create_mapping_nodes(
|
|
2019
|
+
self,
|
|
2020
|
+
node_group: NodeTree,
|
|
2021
|
+
all_expressions: Sequence[tuple[Vrm1ExpressionPropertyGroup, str, str]],
|
|
2022
|
+
armature: Object,
|
|
2023
|
+
material: Material,
|
|
2024
|
+
) -> Mapping[str, ShaderNodeMapping]:
|
|
2025
|
+
mapping_nodes: dict[str, ShaderNodeMapping] = {}
|
|
2026
|
+
for expression, expr_type, expr_name in all_expressions:
|
|
2027
|
+
for bind_index, bind in enumerate(expression.texture_transform_binds):
|
|
2028
|
+
if bind.material == material:
|
|
2029
|
+
mapping_node = node_group.nodes.new(type="ShaderNodeMapping")
|
|
2030
|
+
if not isinstance(mapping_node, ShaderNodeMapping):
|
|
2031
|
+
raise TypeError
|
|
2032
|
+
mapping_node.name = (
|
|
2033
|
+
f"VRM_TextureTransform_Mapping_{expr_type}_{expr_name}"
|
|
2034
|
+
)
|
|
2035
|
+
scale_input_node = mapping_node.inputs["Scale"]
|
|
2036
|
+
if not isinstance(scale_input_node, shader.VECTOR_SOCKET_CLASSES):
|
|
2037
|
+
raise TypeError
|
|
2038
|
+
scale_input_node.default_value = (1, 1, 1)
|
|
2039
|
+
mapping_nodes[f"{expr_type}_{expr_name}"] = mapping_node
|
|
2040
|
+
|
|
2041
|
+
self.setup_mapping_node_drivers(
|
|
2042
|
+
mapping_node,
|
|
2043
|
+
expression,
|
|
2044
|
+
expr_type,
|
|
2045
|
+
expr_name,
|
|
2046
|
+
bind_index,
|
|
2047
|
+
armature,
|
|
2048
|
+
)
|
|
2049
|
+
|
|
2050
|
+
return mapping_nodes
|
|
2051
|
+
|
|
2052
|
+
def setup_mapping_node_drivers(
|
|
2053
|
+
self,
|
|
2054
|
+
mapping_node: ShaderNodeMapping,
|
|
2055
|
+
_expression: Vrm1ExpressionPropertyGroup,
|
|
2056
|
+
expr_type: str,
|
|
2057
|
+
expr_name: str,
|
|
2058
|
+
bind_index: int,
|
|
2059
|
+
armature: Object,
|
|
2060
|
+
) -> None:
|
|
2061
|
+
base_path = f"vrm_addon_extension.vrm1.expressions.{expr_type}"
|
|
2062
|
+
custom_index = f"[{expr_name}]" if expr_type == "custom" else f".{expr_name}"
|
|
2063
|
+
|
|
2064
|
+
# Set all scale values to 0.0
|
|
2065
|
+
scale_input_socket = mapping_node.inputs["Scale"]
|
|
2066
|
+
if not isinstance(scale_input_socket, shader.VECTOR_SOCKET_CLASSES):
|
|
2067
|
+
raise TypeError
|
|
2068
|
+
scale_input_socket.default_value = (0.0, 0.0, 0.0)
|
|
2069
|
+
|
|
2070
|
+
for i, axis in enumerate(["X", "Y"]):
|
|
2071
|
+
for input_name, property_name in (
|
|
2072
|
+
("Location", "offset"),
|
|
2073
|
+
("Scale", "scale"),
|
|
2074
|
+
):
|
|
2075
|
+
fcurve = mapping_node.inputs[input_name].driver_add("default_value", i)
|
|
2076
|
+
if not isinstance(fcurve, FCurve):
|
|
2077
|
+
raise TypeError
|
|
2078
|
+
driver = fcurve.driver
|
|
2079
|
+
if not driver:
|
|
2080
|
+
raise TypeError
|
|
2081
|
+
driver.type = "SCRIPTED"
|
|
2082
|
+
|
|
2083
|
+
var = driver.variables.new()
|
|
2084
|
+
var.name = f"{property_name}_{axis.lower()}"
|
|
2085
|
+
var.type = "SINGLE_PROP"
|
|
2086
|
+
var.targets[0].id_type = "ARMATURE"
|
|
2087
|
+
armature_data = armature.data
|
|
2088
|
+
if not isinstance(armature_data, ID):
|
|
2089
|
+
raise TypeError
|
|
2090
|
+
var.targets[0].id = armature_data
|
|
2091
|
+
data_path = (
|
|
2092
|
+
f"{base_path}{custom_index}"
|
|
2093
|
+
f".texture_transform_binds[{bind_index}].{property_name}[{i}]"
|
|
2094
|
+
)
|
|
2095
|
+
var.targets[0].data_path = data_path
|
|
2096
|
+
|
|
2097
|
+
preview_var = driver.variables.new()
|
|
2098
|
+
preview_var.name = "preview"
|
|
2099
|
+
preview_var.type = "SINGLE_PROP"
|
|
2100
|
+
preview_var.targets[0].id_type = "ARMATURE"
|
|
2101
|
+
preview_var.targets[0].id = armature_data
|
|
2102
|
+
preview_var.targets[0].data_path = f"{base_path}{custom_index}.preview"
|
|
2103
|
+
|
|
2104
|
+
is_binary_var = driver.variables.new()
|
|
2105
|
+
is_binary_var.name = "is_binary"
|
|
2106
|
+
is_binary_var.type = "SINGLE_PROP"
|
|
2107
|
+
is_binary_var.targets[0].id_type = "ARMATURE"
|
|
2108
|
+
is_binary_var.targets[0].id = armature_data
|
|
2109
|
+
is_binary_var.targets[
|
|
2110
|
+
0
|
|
2111
|
+
].data_path = f"{base_path}{custom_index}.is_binary"
|
|
2112
|
+
|
|
2113
|
+
if input_name == "Location":
|
|
2114
|
+
if axis == "X": # Offset UV as expected
|
|
2115
|
+
driver.expression = (
|
|
2116
|
+
f"{property_name}_{axis.lower()} * (1.0 if is_binary and "
|
|
2117
|
+
" preview > 0.5 else (0.0 if is_binary else preview))"
|
|
2118
|
+
)
|
|
2119
|
+
else:
|
|
2120
|
+
# Offset UV in the opposite direction
|
|
2121
|
+
# (this is a quirk of VRM standard)
|
|
2122
|
+
driver.expression = (
|
|
2123
|
+
f"(-{property_name}_{axis.lower()} "
|
|
2124
|
+
" + ( "
|
|
2125
|
+
" 0 if scale_y == 1 "
|
|
2126
|
+
" else (1 - scale_y) "
|
|
2127
|
+
" ) "
|
|
2128
|
+
" ) "
|
|
2129
|
+
" * (1.0 if is_binary and preview > 0.5 "
|
|
2130
|
+
" else (0.0 if is_binary else preview) "
|
|
2131
|
+
" ) "
|
|
2132
|
+
" - (0 if scale_y == 1 else 1)"
|
|
2133
|
+
)
|
|
2134
|
+
elif axis == "X":
|
|
2135
|
+
driver.expression = (
|
|
2136
|
+
f"({property_name}_{axis.lower()} - 1) * "
|
|
2137
|
+
" ( "
|
|
2138
|
+
" 1.0 if is_binary and preview > 0.5 "
|
|
2139
|
+
" else (0.0 if is_binary else preview) "
|
|
2140
|
+
" )"
|
|
2141
|
+
)
|
|
2142
|
+
else:
|
|
2143
|
+
driver.expression = (
|
|
2144
|
+
f"-(2 - {property_name}_{axis.lower()} - 1) "
|
|
2145
|
+
" * (1.0 if is_binary and preview > 0.5 "
|
|
2146
|
+
" else (0.0 if is_binary else preview) "
|
|
2147
|
+
" )"
|
|
2148
|
+
)
|
|
2149
|
+
|
|
2150
|
+
# Add scale_y variable for Y offset calculation
|
|
2151
|
+
if input_name == "Location" and axis == "Y":
|
|
2152
|
+
scale_y_var = driver.variables.new()
|
|
2153
|
+
scale_y_var.name = "scale_y"
|
|
2154
|
+
scale_y_var.type = "SINGLE_PROP"
|
|
2155
|
+
scale_y_var.targets[0].id_type = "ARMATURE"
|
|
2156
|
+
scale_y_var.targets[0].id = armature_data
|
|
2157
|
+
data_path = (
|
|
2158
|
+
f"{base_path}{custom_index}"
|
|
2159
|
+
f".texture_transform_binds[{bind_index}].scale[1]"
|
|
2160
|
+
)
|
|
2161
|
+
scale_y_var.targets[0].data_path = data_path
|
|
2162
|
+
|
|
2163
|
+
def create_blocking_multiply_chains(
|
|
2164
|
+
self,
|
|
2165
|
+
node_group: NodeTree,
|
|
2166
|
+
all_expressions: Sequence[tuple[Vrm1ExpressionPropertyGroup, str, str]],
|
|
2167
|
+
armature: Object,
|
|
2168
|
+
mapping_nodes: Mapping[str, ShaderNodeMapping],
|
|
2169
|
+
) -> Mapping[str, Optional[ShaderNodeVectorMath]]:
|
|
2170
|
+
multiply_chains: dict[str, Optional[ShaderNodeVectorMath]] = {}
|
|
2171
|
+
blockable_types = ["blink", "look", "mouth"]
|
|
2172
|
+
|
|
2173
|
+
for blockable_type in blockable_types:
|
|
2174
|
+
chain = self.create_blocking_multiply_chain(
|
|
2175
|
+
node_group, blockable_type, all_expressions, armature, mapping_nodes
|
|
2176
|
+
)
|
|
2177
|
+
multiply_chains[blockable_type] = chain
|
|
2178
|
+
|
|
2179
|
+
return multiply_chains
|
|
2180
|
+
|
|
2181
|
+
def create_blocking_multiply_chain(
|
|
2182
|
+
self,
|
|
2183
|
+
node_group: NodeTree,
|
|
2184
|
+
blockable_type: str,
|
|
2185
|
+
all_expressions: Sequence[tuple[Vrm1ExpressionPropertyGroup, str, str]],
|
|
2186
|
+
armature: Object,
|
|
2187
|
+
mapping_nodes: Mapping[str, ShaderNodeMapping],
|
|
2188
|
+
) -> Optional[ShaderNodeVectorMath]:
|
|
2189
|
+
blockable_expressions = self.get_blockable_expressions(blockable_type)
|
|
2190
|
+
|
|
2191
|
+
# Add blockable expressions together
|
|
2192
|
+
blockable_add_node = self.add_blockable_expressions(
|
|
2193
|
+
node_group, blockable_expressions, mapping_nodes
|
|
2194
|
+
)
|
|
2195
|
+
|
|
2196
|
+
# Create blocking value nodes
|
|
2197
|
+
value_nodes = self.create_blocking_value_nodes(
|
|
2198
|
+
node_group, blockable_type, all_expressions, armature
|
|
2199
|
+
)
|
|
2200
|
+
|
|
2201
|
+
# Create and link multiply nodes
|
|
2202
|
+
last_multiply = None
|
|
2203
|
+
for i, value_node in enumerate(value_nodes):
|
|
2204
|
+
multiply_node = node_group.nodes.new(type="ShaderNodeVectorMath")
|
|
2205
|
+
if not isinstance(multiply_node, ShaderNodeVectorMath):
|
|
2206
|
+
raise TypeError
|
|
2207
|
+
multiply_node.operation = "MULTIPLY"
|
|
2208
|
+
multiply_node.name = (
|
|
2209
|
+
f"VRM_TextureTransform_BlockingMultiply_{blockable_type}_{i}"
|
|
2210
|
+
)
|
|
2211
|
+
|
|
2212
|
+
if last_multiply:
|
|
2213
|
+
node_group.links.new(last_multiply.outputs[0], multiply_node.inputs[0])
|
|
2214
|
+
elif blockable_add_node:
|
|
2215
|
+
# Connect the blockable add node to the first multiply node
|
|
2216
|
+
node_group.links.new(
|
|
2217
|
+
blockable_add_node.outputs[0], multiply_node.inputs[0]
|
|
2218
|
+
)
|
|
2219
|
+
|
|
2220
|
+
node_group.links.new(value_node.outputs[0], multiply_node.inputs[1])
|
|
2221
|
+
last_multiply = multiply_node
|
|
2222
|
+
|
|
2223
|
+
return (
|
|
2224
|
+
last_multiply or blockable_add_node
|
|
2225
|
+
) # Return blockable_add_node if no multiply nodes were created
|
|
2226
|
+
|
|
2227
|
+
def add_blockable_expressions(
|
|
2228
|
+
self,
|
|
2229
|
+
node_group: NodeTree,
|
|
2230
|
+
blockable_expressions: Sequence[str],
|
|
2231
|
+
mapping_nodes: Mapping[str, ShaderNodeMapping],
|
|
2232
|
+
) -> Optional[ShaderNodeVectorMath]:
|
|
2233
|
+
add_node = None
|
|
2234
|
+
for _i, expr_name in enumerate(blockable_expressions):
|
|
2235
|
+
mapping_node = mapping_nodes.get(f"preset_{expr_name}")
|
|
2236
|
+
if mapping_node:
|
|
2237
|
+
if add_node is None:
|
|
2238
|
+
node = node_group.nodes.new(type="ShaderNodeVectorMath")
|
|
2239
|
+
if not isinstance(node, ShaderNodeVectorMath):
|
|
2240
|
+
raise TypeError
|
|
2241
|
+
add_node = node
|
|
2242
|
+
add_node.operation = "ADD"
|
|
2243
|
+
add_node.name = f"VRM_TextureTransform_BlockableAdd_{expr_name}"
|
|
2244
|
+
node_group.links.new(mapping_node.outputs[0], add_node.inputs[0])
|
|
2245
|
+
else:
|
|
2246
|
+
new_add_node = node_group.nodes.new(type="ShaderNodeVectorMath")
|
|
2247
|
+
if not isinstance(new_add_node, ShaderNodeVectorMath):
|
|
2248
|
+
raise TypeError
|
|
2249
|
+
new_add_node.operation = "ADD"
|
|
2250
|
+
new_add_node.name = f"VRM_TextureTransform_BlockableAdd_{expr_name}"
|
|
2251
|
+
node_group.links.new(add_node.outputs[0], new_add_node.inputs[0])
|
|
2252
|
+
node_group.links.new(
|
|
2253
|
+
mapping_node.outputs[0], new_add_node.inputs[1]
|
|
2254
|
+
)
|
|
2255
|
+
add_node = new_add_node
|
|
2256
|
+
|
|
2257
|
+
return add_node
|
|
2258
|
+
|
|
2259
|
+
def create_blocking_value_nodes(
|
|
2260
|
+
self,
|
|
2261
|
+
node_group: NodeTree,
|
|
2262
|
+
blockable_type: str,
|
|
2263
|
+
all_expressions: Sequence[tuple[object, str, str]],
|
|
2264
|
+
armature: Object,
|
|
2265
|
+
) -> Sequence[ShaderNodeValue]:
|
|
2266
|
+
value_nodes: list[ShaderNodeValue] = []
|
|
2267
|
+
for _expr, expr_type, expr_name in all_expressions:
|
|
2268
|
+
if (
|
|
2269
|
+
expr_type == "preset"
|
|
2270
|
+
and expr_name not in self.get_blockable_expressions(blockable_type)
|
|
2271
|
+
):
|
|
2272
|
+
armature_data = armature.data
|
|
2273
|
+
if not isinstance(armature_data, Armature):
|
|
2274
|
+
raise TypeError
|
|
2275
|
+
override_blockable_type = getattr(
|
|
2276
|
+
getattr(
|
|
2277
|
+
get_armature_extension(armature_data).vrm1.expressions.preset,
|
|
2278
|
+
expr_name,
|
|
2279
|
+
None,
|
|
2280
|
+
),
|
|
2281
|
+
"override_{blockable_type}",
|
|
2282
|
+
None,
|
|
2283
|
+
)
|
|
2284
|
+
include_expression = override_blockable_type != "none"
|
|
2285
|
+
if include_expression:
|
|
2286
|
+
value_node = node_group.nodes.new(type="ShaderNodeValue")
|
|
2287
|
+
if not isinstance(value_node, ShaderNodeValue):
|
|
2288
|
+
raise TypeError
|
|
2289
|
+
value_node.name = (
|
|
2290
|
+
"VRM_TextureTransform_BlockingValue_"
|
|
2291
|
+
f"{blockable_type}_{expr_name}"
|
|
2292
|
+
)
|
|
2293
|
+
value_nodes.append(value_node)
|
|
2294
|
+
|
|
2295
|
+
fcurve = value_node.outputs[0].driver_add("default_value")
|
|
2296
|
+
if not isinstance(fcurve, FCurve):
|
|
2297
|
+
raise TypeError
|
|
2298
|
+
driver = fcurve.driver
|
|
2299
|
+
if not driver:
|
|
2300
|
+
raise TypeError
|
|
2301
|
+
driver.type = "SCRIPTED"
|
|
2302
|
+
preview_var = driver.variables.new()
|
|
2303
|
+
preview_var.name = "preview"
|
|
2304
|
+
preview_var.type = "SINGLE_PROP"
|
|
2305
|
+
preview_var.targets[0].id_type = "ARMATURE"
|
|
2306
|
+
armature_data = armature.data
|
|
2307
|
+
if not isinstance(armature_data, ID):
|
|
2308
|
+
raise TypeError
|
|
2309
|
+
preview_var.targets[0].id = armature_data
|
|
2310
|
+
data_path = (
|
|
2311
|
+
"vrm_addon_extension.vrm1.expressions.preset"
|
|
2312
|
+
f".{expr_name}.preview"
|
|
2313
|
+
)
|
|
2314
|
+
preview_var.targets[0].data_path = data_path
|
|
2315
|
+
driver.expression = "0 if preview >= 0.5 else 1"
|
|
2316
|
+
|
|
2317
|
+
return value_nodes
|
|
2318
|
+
|
|
2319
|
+
def create_add_node(
|
|
2320
|
+
self,
|
|
2321
|
+
node_group: NodeTree,
|
|
2322
|
+
input_node: Node,
|
|
2323
|
+
previous_add_node: Optional[ShaderNodeVectorMath],
|
|
2324
|
+
) -> ShaderNodeVectorMath:
|
|
2325
|
+
add_node = node_group.nodes.new(type="ShaderNodeVectorMath")
|
|
2326
|
+
if not isinstance(add_node, ShaderNodeVectorMath):
|
|
2327
|
+
raise TypeError
|
|
2328
|
+
add_node.operation = "ADD"
|
|
2329
|
+
add_node.name = f"VRM_TextureTransform_Add_{input_node.name}"
|
|
2330
|
+
|
|
2331
|
+
node_group.links.new(input_node.outputs[0], add_node.inputs[0])
|
|
2332
|
+
if previous_add_node:
|
|
2333
|
+
node_group.links.new(previous_add_node.outputs[0], add_node.inputs[1])
|
|
2334
|
+
|
|
2335
|
+
return add_node
|
|
2336
|
+
|
|
2337
|
+
def create_final_add_chain(
|
|
2338
|
+
self,
|
|
2339
|
+
node_group: NodeTree,
|
|
2340
|
+
mapping_nodes: Mapping[str, Node],
|
|
2341
|
+
multiply_chains: Mapping[str, Optional[ShaderNodeVectorMath]],
|
|
2342
|
+
all_expressions: Sequence[tuple[object, str, str]],
|
|
2343
|
+
) -> Optional[ShaderNodeVectorMath]:
|
|
2344
|
+
add_nodes: list[ShaderNodeVectorMath] = []
|
|
2345
|
+
last_add = None
|
|
2346
|
+
|
|
2347
|
+
# Add non-blockable mapping nodes
|
|
2348
|
+
for _expr, expr_type, expr_name in all_expressions:
|
|
2349
|
+
if not self.is_blockable_expression(expr_name):
|
|
2350
|
+
mapping_node = mapping_nodes.get(f"{expr_type}_{expr_name}")
|
|
2351
|
+
if mapping_node:
|
|
2352
|
+
add_node = self.create_add_node(node_group, mapping_node, last_add)
|
|
2353
|
+
last_add = add_node
|
|
2354
|
+
add_nodes.append(add_node)
|
|
2355
|
+
|
|
2356
|
+
# Add results from multiply chains
|
|
2357
|
+
for last_multiply in multiply_chains.values():
|
|
2358
|
+
if last_multiply:
|
|
2359
|
+
add_node = self.create_add_node(node_group, last_multiply, last_add)
|
|
2360
|
+
last_add = add_node
|
|
2361
|
+
add_nodes.append(add_node)
|
|
2362
|
+
|
|
2363
|
+
return last_add
|
|
2364
|
+
|
|
2365
|
+
def is_blockable_expression(self, expr_name: str) -> bool:
|
|
2366
|
+
blockable_types = ["blink", "look", "mouth"]
|
|
2367
|
+
return any(
|
|
2368
|
+
expr_name in self.get_blockable_expressions(bt) for bt in blockable_types
|
|
2369
|
+
)
|
|
2370
|
+
|
|
2371
|
+
def get_blockable_expressions(self, blockable_type: str) -> Sequence[str]:
|
|
2372
|
+
if blockable_type == "blink":
|
|
2373
|
+
return ["blink", "blink_left", "blink_right"]
|
|
2374
|
+
if blockable_type == "look":
|
|
2375
|
+
return ["look_up", "look_down", "look_left", "look_right"]
|
|
2376
|
+
if blockable_type == "mouth":
|
|
2377
|
+
return ["aa", "ih", "ee", "oh", "ou"]
|
|
2378
|
+
return []
|
|
2379
|
+
|
|
2380
|
+
def remove_existing_nodes(self, node_tree: ShaderNodeTree) -> None:
|
|
2381
|
+
nodes_to_remove: list[Node] = []
|
|
2382
|
+
links_to_restore: list[tuple[NodeSocket, NodeSocket]] = []
|
|
2383
|
+
|
|
2384
|
+
for node in node_tree.nodes:
|
|
2385
|
+
if node.name.startswith("VRM_TextureTransform_"):
|
|
2386
|
+
nodes_to_remove.append(node)
|
|
2387
|
+
# Store information about links to restore
|
|
2388
|
+
for output in node.outputs:
|
|
2389
|
+
for link in output.links:
|
|
2390
|
+
if not link.to_node.name.startswith("VRM_TextureTransform_"):
|
|
2391
|
+
# Find the first input that doesn't start
|
|
2392
|
+
# with VRM_TextureTransform_
|
|
2393
|
+
for input_socket in node.inputs:
|
|
2394
|
+
if input_socket.is_linked and not input_socket.links[
|
|
2395
|
+
0
|
|
2396
|
+
].from_node.name.startswith("VRM_TextureTransform_"):
|
|
2397
|
+
links_to_restore.append(
|
|
2398
|
+
(
|
|
2399
|
+
input_socket.links[0].from_socket,
|
|
2400
|
+
link.to_socket,
|
|
2401
|
+
)
|
|
2402
|
+
)
|
|
2403
|
+
break
|
|
2404
|
+
elif (
|
|
2405
|
+
node.type == "GROUP"
|
|
2406
|
+
and isinstance(node, ShaderNodeGroup)
|
|
2407
|
+
and (node_node_tree := node.node_tree)
|
|
2408
|
+
and node_node_tree.name.startswith("VRM_TextureTransform_")
|
|
2409
|
+
):
|
|
2410
|
+
nodes_to_remove.append(node)
|
|
2411
|
+
# Handle links for group nodes
|
|
2412
|
+
for output in node.outputs:
|
|
2413
|
+
links_to_restore.extend(
|
|
2414
|
+
(node.inputs[0].links[0].from_socket, link.to_socket)
|
|
2415
|
+
for link in output.links
|
|
2416
|
+
if not link.to_node.name.startswith("VRM_TextureTransform_")
|
|
2417
|
+
)
|
|
2418
|
+
|
|
2419
|
+
# Remove nodes
|
|
2420
|
+
for node in nodes_to_remove:
|
|
2421
|
+
node_tree.nodes.remove(node)
|
|
2422
|
+
|
|
2423
|
+
# Restore links
|
|
2424
|
+
for from_socket, to_socket in links_to_restore:
|
|
2425
|
+
node_tree.links.new(from_socket, to_socket)
|
|
2426
|
+
|
|
2427
|
+
# Remove orphaned node groups
|
|
2428
|
+
for group in bpy.data.node_groups:
|
|
2429
|
+
if group.name.startswith("VRM_TextureTransform_") and not group.users:
|
|
2430
|
+
bpy.data.node_groups.remove(group)
|
|
2431
|
+
|
|
2432
|
+
def connect_group_to_image_node(
|
|
2433
|
+
self,
|
|
2434
|
+
node_tree: ShaderNodeTree,
|
|
2435
|
+
group_node: ShaderNodeGroup,
|
|
2436
|
+
image_node: ShaderNodeTexImage,
|
|
2437
|
+
) -> None:
|
|
2438
|
+
vector_input = image_node.inputs["Vector"]
|
|
2439
|
+
existing_links = vector_input.links
|
|
2440
|
+
|
|
2441
|
+
if not existing_links:
|
|
2442
|
+
node_tree.links.new(group_node.outputs[0], vector_input)
|
|
2443
|
+
else:
|
|
2444
|
+
vector_math_node = node_tree.nodes.new(type="ShaderNodeVectorMath")
|
|
2445
|
+
if not isinstance(vector_math_node, ShaderNodeVectorMath):
|
|
2446
|
+
raise TypeError
|
|
2447
|
+
vector_math_node.name = f"VRM_TextureTransform_VectorMath_{image_node.name}"
|
|
2448
|
+
vector_math_node.operation = "ADD"
|
|
2449
|
+
|
|
2450
|
+
node_tree.links.new(group_node.outputs[0], vector_math_node.inputs[0])
|
|
2451
|
+
node_tree.links.new(
|
|
2452
|
+
existing_links[0].from_socket, vector_math_node.inputs[1]
|
|
2453
|
+
)
|
|
2454
|
+
node_tree.links.remove(existing_links[0])
|
|
2455
|
+
node_tree.links.new(vector_math_node.outputs[0], vector_input)
|
|
2456
|
+
|
|
2457
|
+
if TYPE_CHECKING:
|
|
2458
|
+
# This code is auto generated.
|
|
2459
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
2460
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
2461
|
+
expression_name: str # type: ignore[no-redef]
|
|
2462
|
+
|
|
2463
|
+
|
|
2464
|
+
def draw_bone_prop_search(
|
|
2465
|
+
layout: UILayout,
|
|
2466
|
+
human_bone_specification: HumanBoneSpecification,
|
|
2467
|
+
armature: Object,
|
|
2468
|
+
*,
|
|
2469
|
+
show_diagnostics: bool = True,
|
|
2470
|
+
) -> None:
|
|
2471
|
+
armature_data = armature.data
|
|
2472
|
+
if not isinstance(armature_data, Armature):
|
|
2473
|
+
return
|
|
2474
|
+
ext = get_armature_extension(armature_data)
|
|
2475
|
+
human_bones = ext.vrm1.humanoid.human_bones
|
|
2476
|
+
human_bone = human_bones.human_bone_name_to_human_bone().get(
|
|
2477
|
+
human_bone_specification.name
|
|
2478
|
+
)
|
|
2479
|
+
if not human_bone:
|
|
2480
|
+
return
|
|
2481
|
+
row = VRM_MT_bone_assignment.draw_input_layout(
|
|
2482
|
+
layout,
|
|
2483
|
+
human_bone.node,
|
|
2484
|
+
text="",
|
|
2485
|
+
icon=human_bone_specification.icon,
|
|
2486
|
+
simple=not human_bones.filter_by_human_bone_hierarchy,
|
|
2487
|
+
)
|
|
2488
|
+
|
|
2489
|
+
if not show_diagnostics:
|
|
2490
|
+
return
|
|
2491
|
+
|
|
2492
|
+
bone_name = human_bone.node.bone_name
|
|
2493
|
+
if human_bones.filter_by_human_bone_hierarchy:
|
|
2494
|
+
if bone_name and bone_name not in human_bone.node.bone_name_candidates:
|
|
2495
|
+
icon = "ERROR"
|
|
2496
|
+
row.alert = True
|
|
2497
|
+
else:
|
|
2498
|
+
icon = "VIEWZOOM"
|
|
2499
|
+
elif bone_name and any(
|
|
2500
|
+
it.node.bone_name == bone_name and it != human_bone
|
|
2501
|
+
for it in human_bones.human_bone_name_to_human_bone().values()
|
|
2502
|
+
):
|
|
2503
|
+
icon = "ERROR"
|
|
2504
|
+
row.alert = True
|
|
2505
|
+
else:
|
|
2506
|
+
return
|
|
2507
|
+
|
|
2508
|
+
op = layout_operator(
|
|
2509
|
+
row,
|
|
2510
|
+
VRM_OT_show_vrm1_bone_assignment_diagnostics,
|
|
2511
|
+
text="",
|
|
2512
|
+
translate=False,
|
|
2513
|
+
icon=icon,
|
|
2514
|
+
)
|
|
2515
|
+
op.armature_object_name = armature.name
|
|
2516
|
+
op.human_bone_name = human_bone_specification.name.value
|
|
2517
|
+
row.separator(factor=0.5)
|
|
2518
|
+
|
|
2519
|
+
|
|
2520
|
+
class VRM_OT_show_vrm1_bone_assignment_diagnostics(Operator):
|
|
2521
|
+
bl_idname = "vrm.show_vrm1_bone_assignment_diagnostics"
|
|
2522
|
+
bl_label = "Bone Assignment Diagnostics"
|
|
2523
|
+
bl_description = (
|
|
2524
|
+
"Shows the cause of the bone assignment error"
|
|
2525
|
+
+ " or the reason why none of the bone assignment candidates exist."
|
|
2526
|
+
)
|
|
2527
|
+
|
|
2528
|
+
def execute(self, _context: Context) -> set[str]:
|
|
2529
|
+
return {"FINISHED"}
|
|
2530
|
+
|
|
2531
|
+
armature_object_name: StringProperty( # type: ignore[valid-type]
|
|
2532
|
+
options={"HIDDEN"}
|
|
2533
|
+
)
|
|
2534
|
+
|
|
2535
|
+
human_bone_name: StringProperty( # type: ignore[valid-type]
|
|
2536
|
+
options={"HIDDEN"}
|
|
2537
|
+
)
|
|
2538
|
+
|
|
2539
|
+
def invoke(self, context: Context, _event: Event) -> set[str]:
|
|
2540
|
+
obj = context.blend_data.objects.get(self.armature_object_name)
|
|
2541
|
+
if not obj or obj.type != "ARMATURE":
|
|
2542
|
+
return {"CANCELLED"}
|
|
2543
|
+
|
|
2544
|
+
if not HumanBoneName.from_str(self.human_bone_name):
|
|
2545
|
+
return {"CANCELLED"}
|
|
2546
|
+
|
|
2547
|
+
return context.window_manager.invoke_props_dialog(self, width=800)
|
|
2548
|
+
|
|
2549
|
+
def draw(self, context: Context) -> None:
|
|
2550
|
+
armature = context.blend_data.objects.get(self.armature_object_name)
|
|
2551
|
+
if not armature:
|
|
2552
|
+
return
|
|
2553
|
+
armature_data = armature.data
|
|
2554
|
+
if not isinstance(armature_data, Armature):
|
|
2555
|
+
return
|
|
2556
|
+
human_bone_name = HumanBoneName.from_str(self.human_bone_name)
|
|
2557
|
+
if not human_bone_name:
|
|
2558
|
+
return
|
|
2559
|
+
human_bone_specification = HumanBoneSpecifications.get(human_bone_name)
|
|
2560
|
+
|
|
2561
|
+
layout = self.layout.box()
|
|
2562
|
+
head_row = layout.row()
|
|
2563
|
+
head_row.alignment = "LEFT"
|
|
2564
|
+
head_row.label(
|
|
2565
|
+
text=pgettext('Assignment of the VRM Human Bone "{vrm_human_bone}"').format(
|
|
2566
|
+
vrm_human_bone=human_bone_specification.title
|
|
2567
|
+
),
|
|
2568
|
+
icon="ARMATURE_DATA",
|
|
2569
|
+
)
|
|
2570
|
+
draw_bone_prop_search(
|
|
2571
|
+
head_row, human_bone_specification, armature, show_diagnostics=False
|
|
2572
|
+
)
|
|
2573
|
+
help_op = layout_operator(
|
|
2574
|
+
head_row,
|
|
2575
|
+
VRM_OT_open_url_in_web_browser,
|
|
2576
|
+
icon="URL",
|
|
2577
|
+
text="Open help in a Web Browser",
|
|
2578
|
+
)
|
|
2579
|
+
help_op.url = pgettext(
|
|
2580
|
+
"https://github.com/vrm-c/vrm-specification/blob/c24d76d99a18738dd2c266be1c83f089064a7b5e/specification/VRMC_vrm-1.0/humanoid.md#humanoid-bone-parent-child-relationship"
|
|
2581
|
+
)
|
|
2582
|
+
|
|
2583
|
+
ext = get_armature_extension(armature_data)
|
|
2584
|
+
humanoid = ext.vrm1.humanoid
|
|
2585
|
+
human_bones = humanoid.human_bones
|
|
2586
|
+
human_bone_name_to_human_bone = human_bones.human_bone_name_to_human_bone()
|
|
2587
|
+
human_bone = human_bone_name_to_human_bone.get(human_bone_name)
|
|
2588
|
+
if not human_bone:
|
|
2589
|
+
return
|
|
2590
|
+
|
|
2591
|
+
if not human_bones.filter_by_human_bone_hierarchy:
|
|
2592
|
+
error_messages = human_bones.human_bone_duplication_error_messages()
|
|
2593
|
+
for bone_name, line in error_messages:
|
|
2594
|
+
if human_bone.node.bone_name == bone_name:
|
|
2595
|
+
layout.label(text=line, icon="ERROR", translate=False)
|
|
2596
|
+
if not error_messages and human_bone.node.bone_name:
|
|
2597
|
+
text = pgettext(
|
|
2598
|
+
'The bone "{bone_name}" of the armature "{armature_object_name}"'
|
|
2599
|
+
+ ' can be assigned to the VRM Human Bone "{human_bone_name}".'
|
|
2600
|
+
).format(
|
|
2601
|
+
armature_object_name=self.armature_object_name,
|
|
2602
|
+
bone_name=human_bone.node.bone_name,
|
|
2603
|
+
human_bone_name=human_bone_specification.title,
|
|
2604
|
+
)
|
|
2605
|
+
layout.label(
|
|
2606
|
+
text=text,
|
|
2607
|
+
translate=False,
|
|
2608
|
+
icon="CHECKMARK",
|
|
2609
|
+
)
|
|
2610
|
+
return
|
|
2611
|
+
|
|
2612
|
+
bone_name = human_bone.node.bone_name
|
|
2613
|
+
if bone_name in human_bone.node.bone_name_candidates:
|
|
2614
|
+
layout.label(
|
|
2615
|
+
text=pgettext(
|
|
2616
|
+
'The bone "{bone_name}" of the armature "{armature_object_name}"'
|
|
2617
|
+
+ ' can be assigned to the VRM Human Bone "{human_bone_name}".'
|
|
2618
|
+
).format(
|
|
2619
|
+
armature_object_name=self.armature_object_name,
|
|
2620
|
+
bone_name=bone_name,
|
|
2621
|
+
human_bone_name=human_bone_specification.title,
|
|
2622
|
+
),
|
|
2623
|
+
translate=False,
|
|
2624
|
+
icon="CHECKMARK",
|
|
2625
|
+
)
|
|
2626
|
+
elif bone_name:
|
|
2627
|
+
layout.label(
|
|
2628
|
+
text=pgettext(
|
|
2629
|
+
'The bone "{bone_name}" of the armature "{armature_object_name}"'
|
|
2630
|
+
+ ' cannot be assigned to the VRM Human Bone "{human_bone_name}".'
|
|
2631
|
+
).format(
|
|
2632
|
+
armature_object_name=self.armature_object_name,
|
|
2633
|
+
bone_name=bone_name,
|
|
2634
|
+
human_bone_name=human_bone_specification.title,
|
|
2635
|
+
),
|
|
2636
|
+
translate=False,
|
|
2637
|
+
icon="ERROR",
|
|
2638
|
+
)
|
|
2639
|
+
elif not human_bone.node.bone_name_candidates:
|
|
2640
|
+
layout.label(
|
|
2641
|
+
text=pgettext(
|
|
2642
|
+
'The armature "{armature_object_name}" does not have any bones'
|
|
2643
|
+
+ ' that can be assigned to the VRM Human Bone "{human_bone_name}".'
|
|
2644
|
+
).format(
|
|
2645
|
+
armature_object_name=self.armature_object_name,
|
|
2646
|
+
human_bone_name=human_bone_specification.title,
|
|
2647
|
+
),
|
|
2648
|
+
translate=False,
|
|
2649
|
+
icon="ERROR",
|
|
2650
|
+
)
|
|
2651
|
+
|
|
2652
|
+
bpy_bone_name_to_human_bone_specification = {
|
|
2653
|
+
bpy_bone_name: HumanBoneSpecifications.get(human_bone_name)
|
|
2654
|
+
for human_bone_name, human_bone in human_bone_name_to_human_bone.items()
|
|
2655
|
+
if (bpy_bone_name := human_bone.node.bone_name)
|
|
2656
|
+
}
|
|
2657
|
+
error_bpy_bone_names = [
|
|
2658
|
+
bpy_bone_name
|
|
2659
|
+
for human_bone in human_bone_name_to_human_bone.values()
|
|
2660
|
+
if (bpy_bone_name := human_bone.node.bone_name)
|
|
2661
|
+
and bpy_bone_name not in human_bone.node.bone_name_candidates
|
|
2662
|
+
]
|
|
2663
|
+
|
|
2664
|
+
Vrm1HumanBonesPropertyGroup.update_all_bone_name_candidates(
|
|
2665
|
+
context, armature_data.name
|
|
2666
|
+
)
|
|
2667
|
+
HumanoidStructureBonePropertyGroup.find_bone_candidates(
|
|
2668
|
+
armature_data,
|
|
2669
|
+
human_bone_specification,
|
|
2670
|
+
bpy_bone_name_to_human_bone_specification,
|
|
2671
|
+
error_bpy_bone_names,
|
|
2672
|
+
layout,
|
|
2673
|
+
)
|
|
2674
|
+
|
|
2675
|
+
if TYPE_CHECKING:
|
|
2676
|
+
# This code is auto generated.
|
|
2677
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
2678
|
+
armature_object_name: str # type: ignore[no-redef]
|
|
2679
|
+
human_bone_name: str # type: ignore[no-redef]
|