@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.
Files changed (245) hide show
  1. package/LICENSE +16 -0
  2. package/README.md +4 -11
  3. package/browser/addons/vrm/LICENSE_(OPTION1)_MIT.txt +22 -0
  4. package/browser/addons/vrm/LICENSE_(OPTION2)_GPL-3_0.txt +674 -0
  5. package/browser/addons/vrm/LICENSE_MAIN.txt +1 -0
  6. package/browser/addons/vrm/README.txt +3 -0
  7. package/browser/addons/vrm/__init__.py +41 -0
  8. package/browser/addons/vrm/blender_manifest.toml +16 -0
  9. package/browser/addons/vrm/common/__init__.py +1 -0
  10. package/browser/addons/vrm/common/animation.py +58 -0
  11. package/browser/addons/vrm/common/blender_manifest.py +82 -0
  12. package/browser/addons/vrm/common/char.py +16 -0
  13. package/browser/addons/vrm/common/convert.py +287 -0
  14. package/browser/addons/vrm/common/convert_any.py +48 -0
  15. package/browser/addons/vrm/common/debug.py +40 -0
  16. package/browser/addons/vrm/common/deep.py +162 -0
  17. package/browser/addons/vrm/common/error_dialog.py +274 -0
  18. package/browser/addons/vrm/common/fs.py +71 -0
  19. package/browser/addons/vrm/common/gl.py +20 -0
  20. package/browser/addons/vrm/common/gltf.py +1149 -0
  21. package/browser/addons/vrm/common/human_bone_mapper/__init__.py +1 -0
  22. package/browser/addons/vrm/common/human_bone_mapper/biped_mapping.py +84 -0
  23. package/browser/addons/vrm/common/human_bone_mapper/cats_blender_plugin_fix_model_mapping.py +63 -0
  24. package/browser/addons/vrm/common/human_bone_mapper/human_bone_mapper.py +203 -0
  25. package/browser/addons/vrm/common/human_bone_mapper/microsoft_rocketbox_mapping.py +69 -0
  26. package/browser/addons/vrm/common/human_bone_mapper/mixamo_mapping.py +62 -0
  27. package/browser/addons/vrm/common/human_bone_mapper/mmd_mapping.py +115 -0
  28. package/browser/addons/vrm/common/human_bone_mapper/mpfb_mapping.py +120 -0
  29. package/browser/addons/vrm/common/human_bone_mapper/ready_player_me_mapping.py +65 -0
  30. package/browser/addons/vrm/common/human_bone_mapper/reallusion_mapping.py +94 -0
  31. package/browser/addons/vrm/common/human_bone_mapper/rigify_meta_rig_mapping.py +65 -0
  32. package/browser/addons/vrm/common/human_bone_mapper/structure_based_mapping.py +891 -0
  33. package/browser/addons/vrm/common/human_bone_mapper/unreal_mapping.py +62 -0
  34. package/browser/addons/vrm/common/human_bone_mapper/vrm_addon_mapping.py +112 -0
  35. package/browser/addons/vrm/common/human_bone_mapper/vroid_mapping.py +89 -0
  36. package/browser/addons/vrm/common/legacy_gltf.py +16 -0
  37. package/browser/addons/vrm/common/logger.py +84 -0
  38. package/browser/addons/vrm/common/mtoon0.blend +0 -0
  39. package/browser/addons/vrm/common/mtoon1.blend +0 -0
  40. package/browser/addons/vrm/common/mtoon1_outline.blend +0 -0
  41. package/browser/addons/vrm/common/mtoon_auto_setup.blend +0 -0
  42. package/browser/addons/vrm/common/mtoon_unversioned.py +62 -0
  43. package/browser/addons/vrm/common/ops/__init__.py +10 -0
  44. package/browser/addons/vrm/common/ops/export_scene.py +67 -0
  45. package/browser/addons/vrm/common/ops/icyp.py +53 -0
  46. package/browser/addons/vrm/common/ops/import_scene.py +57 -0
  47. package/browser/addons/vrm/common/ops/vrm.py +2380 -0
  48. package/browser/addons/vrm/common/ops/wm.py +167 -0
  49. package/browser/addons/vrm/common/preferences.py +362 -0
  50. package/browser/addons/vrm/common/progress.py +87 -0
  51. package/browser/addons/vrm/common/rotation.py +89 -0
  52. package/browser/addons/vrm/common/safe_removal.py +46 -0
  53. package/browser/addons/vrm/common/scene_watcher.py +230 -0
  54. package/browser/addons/vrm/common/shader.py +2243 -0
  55. package/browser/addons/vrm/common/shape_key_mapper/__init__.py +1 -0
  56. package/browser/addons/vrm/common/shape_key_mapper/arkit_mapping.py +168 -0
  57. package/browser/addons/vrm/common/shape_key_mapper/mmd_mapping.py +62 -0
  58. package/browser/addons/vrm/common/shape_key_mapper/ready_player_me_mapping.py +52 -0
  59. package/browser/addons/vrm/common/shape_key_mapper/vrchat_mapping.py +17 -0
  60. package/browser/addons/vrm/common/third_party_user_extension.py +214 -0
  61. package/browser/addons/vrm/common/transparent_z_write.blend +0 -0
  62. package/browser/addons/vrm/common/version.py +161 -0
  63. package/browser/addons/vrm/common/vrm0/__init__.py +1 -0
  64. package/browser/addons/vrm/common/vrm0/human_bone.py +662 -0
  65. package/browser/addons/vrm/common/vrm0/material_property.py +346 -0
  66. package/browser/addons/vrm/common/vrm1/__init__.py +1 -0
  67. package/browser/addons/vrm/common/vrm1/expression_preset.py +207 -0
  68. package/browser/addons/vrm/common/vrm1/human_bone.py +822 -0
  69. package/browser/addons/vrm/common/workspace.py +227 -0
  70. package/browser/addons/vrm/common/writable_context.py +77 -0
  71. package/browser/addons/vrm/documentation/en-us/animation/index.md +63 -0
  72. package/browser/addons/vrm/documentation/en-us/create-humanoid-vrm-from-scratch/index.md +131 -0
  73. package/browser/addons/vrm/documentation/en-us/create-simple-vrm-from-scratch/index.md +30 -0
  74. package/browser/addons/vrm/documentation/en-us/development/index.md +136 -0
  75. package/browser/addons/vrm/documentation/en-us/index.md +41 -0
  76. package/browser/addons/vrm/documentation/en-us/installation/index.md +152 -0
  77. package/browser/addons/vrm/documentation/en-us/material-mtoon/index.md +122 -0
  78. package/browser/addons/vrm/documentation/en-us/material-pbr/index.md +94 -0
  79. package/browser/addons/vrm/documentation/en-us/scripting-api/index.md +391 -0
  80. package/browser/addons/vrm/documentation/en-us/ui/export_scene.vrm/index.md +57 -0
  81. package/browser/addons/vrm/documentation/en-us/ui/export_scene.vrma/index.md +17 -0
  82. package/browser/addons/vrm/documentation/en-us/ui/import_scene.vrm/index.md +61 -0
  83. package/browser/addons/vrm/documentation/en-us/ui/import_scene.vrma/index.md +13 -0
  84. package/browser/addons/vrm/documentation/ja-jp/animation/index.md +57 -0
  85. package/browser/addons/vrm/documentation/ja-jp/create-humanoid-vrm-from-scratch/index.md +129 -0
  86. package/browser/addons/vrm/documentation/ja-jp/create-simple-vrm-from-scratch/index.md +29 -0
  87. package/browser/addons/vrm/documentation/ja-jp/development/index.md +137 -0
  88. package/browser/addons/vrm/documentation/ja-jp/index.md +37 -0
  89. package/browser/addons/vrm/documentation/ja-jp/installation/index.md +150 -0
  90. package/browser/addons/vrm/documentation/ja-jp/material-mtoon/index.md +113 -0
  91. package/browser/addons/vrm/documentation/ja-jp/material-pbr/index.md +90 -0
  92. package/browser/addons/vrm/documentation/ja-jp/scripting-api/index.md +389 -0
  93. package/browser/addons/vrm/documentation/ja-jp/ui/export_scene.vrm/index.md +55 -0
  94. package/browser/addons/vrm/documentation/ja-jp/ui/export_scene.vrma/index.md +19 -0
  95. package/browser/addons/vrm/documentation/ja-jp/ui/import_scene.vrm/index.md +56 -0
  96. package/browser/addons/vrm/documentation/ja-jp/ui/import_scene.vrma/index.md +14 -0
  97. package/browser/addons/vrm/editor/__init__.py +1 -0
  98. package/browser/addons/vrm/editor/extension.py +425 -0
  99. package/browser/addons/vrm/editor/extension_accessor.py +75 -0
  100. package/browser/addons/vrm/editor/handler.py +84 -0
  101. package/browser/addons/vrm/editor/khr_character/__init__.py +1 -0
  102. package/browser/addons/vrm/editor/khr_character/panel.py +69 -0
  103. package/browser/addons/vrm/editor/khr_character/property_group.py +27 -0
  104. package/browser/addons/vrm/editor/khr_xmp_json_ld/__init__.py +1 -0
  105. package/browser/addons/vrm/editor/khr_xmp_json_ld/ops.py +464 -0
  106. package/browser/addons/vrm/editor/khr_xmp_json_ld/panel.py +136 -0
  107. package/browser/addons/vrm/editor/khr_xmp_json_ld/property_group.py +83 -0
  108. package/browser/addons/vrm/editor/khr_xmp_json_ld/ui_list.py +198 -0
  109. package/browser/addons/vrm/editor/make_armature.py +709 -0
  110. package/browser/addons/vrm/editor/menu.py +147 -0
  111. package/browser/addons/vrm/editor/migration.py +297 -0
  112. package/browser/addons/vrm/editor/mtoon1/__init__.py +1 -0
  113. package/browser/addons/vrm/editor/mtoon1/handler.py +20 -0
  114. package/browser/addons/vrm/editor/mtoon1/migration.py +536 -0
  115. package/browser/addons/vrm/editor/mtoon1/ops.py +304 -0
  116. package/browser/addons/vrm/editor/mtoon1/panel.py +442 -0
  117. package/browser/addons/vrm/editor/mtoon1/property_group.py +4977 -0
  118. package/browser/addons/vrm/editor/mtoon1/scene_watcher.py +381 -0
  119. package/browser/addons/vrm/editor/node_constraint1/__init__.py +1 -0
  120. package/browser/addons/vrm/editor/node_constraint1/panel.py +317 -0
  121. package/browser/addons/vrm/editor/node_constraint1/property_group.py +25 -0
  122. package/browser/addons/vrm/editor/ops.py +471 -0
  123. package/browser/addons/vrm/editor/panel.py +315 -0
  124. package/browser/addons/vrm/editor/property_group.py +888 -0
  125. package/browser/addons/vrm/editor/search.py +682 -0
  126. package/browser/addons/vrm/editor/spring_bone1/__init__.py +1 -0
  127. package/browser/addons/vrm/editor/spring_bone1/handler.py +994 -0
  128. package/browser/addons/vrm/editor/spring_bone1/menu.py +246 -0
  129. package/browser/addons/vrm/editor/spring_bone1/migration.py +86 -0
  130. package/browser/addons/vrm/editor/spring_bone1/ops.py +1679 -0
  131. package/browser/addons/vrm/editor/spring_bone1/panel.py +605 -0
  132. package/browser/addons/vrm/editor/spring_bone1/property_group.py +1457 -0
  133. package/browser/addons/vrm/editor/spring_bone1/ui_list.py +408 -0
  134. package/browser/addons/vrm/editor/subscription.py +123 -0
  135. package/browser/addons/vrm/editor/t_pose.py +713 -0
  136. package/browser/addons/vrm/editor/validation.py +1271 -0
  137. package/browser/addons/vrm/editor/vrm0/__init__.py +1 -0
  138. package/browser/addons/vrm/editor/vrm0/gizmo_group.py +63 -0
  139. package/browser/addons/vrm/editor/vrm0/handler.py +23 -0
  140. package/browser/addons/vrm/editor/vrm0/menu.py +145 -0
  141. package/browser/addons/vrm/editor/vrm0/migration.py +761 -0
  142. package/browser/addons/vrm/editor/vrm0/ops.py +2145 -0
  143. package/browser/addons/vrm/editor/vrm0/panel.py +1333 -0
  144. package/browser/addons/vrm/editor/vrm0/property_group.py +1818 -0
  145. package/browser/addons/vrm/editor/vrm0/ui_list.py +688 -0
  146. package/browser/addons/vrm/editor/vrm1/__init__.py +1 -0
  147. package/browser/addons/vrm/editor/vrm1/handler.py +28 -0
  148. package/browser/addons/vrm/editor/vrm1/menu.py +76 -0
  149. package/browser/addons/vrm/editor/vrm1/migration.py +321 -0
  150. package/browser/addons/vrm/editor/vrm1/ops.py +2679 -0
  151. package/browser/addons/vrm/editor/vrm1/panel.py +1243 -0
  152. package/browser/addons/vrm/editor/vrm1/property_group.py +2087 -0
  153. package/browser/addons/vrm/editor/vrm1/scene_watcher.py +90 -0
  154. package/browser/addons/vrm/editor/vrm1/ui_list.py +533 -0
  155. package/browser/addons/vrm/exporter/__init__.py +1 -0
  156. package/browser/addons/vrm/exporter/abstract_base_vrm_exporter.py +628 -0
  157. package/browser/addons/vrm/exporter/export_scene.py +930 -0
  158. package/browser/addons/vrm/exporter/gltf2_export_user_extension.py +45 -0
  159. package/browser/addons/vrm/exporter/khr_character_exporter.py +34 -0
  160. package/browser/addons/vrm/exporter/vrm0_exporter.py +4020 -0
  161. package/browser/addons/vrm/exporter/vrm1_exporter.py +3682 -0
  162. package/browser/addons/vrm/exporter/vrm_animation_exporter.py +1035 -0
  163. package/browser/addons/vrm/external/__init__.py +1 -0
  164. package/browser/addons/vrm/external/auto_rig_pro_support.py +3 -0
  165. package/browser/addons/vrm/external/io_scene_gltf2_support.py +418 -0
  166. package/browser/addons/vrm/external/rigify_support.py +1 -0
  167. package/browser/addons/vrm/importer/__init__.py +1 -0
  168. package/browser/addons/vrm/importer/abstract_base_vrm_importer.py +1548 -0
  169. package/browser/addons/vrm/importer/file_handler.py +34 -0
  170. package/browser/addons/vrm/importer/gltf2_import_user_extension.py +268 -0
  171. package/browser/addons/vrm/importer/import_scene.py +646 -0
  172. package/browser/addons/vrm/importer/license_validation.py +198 -0
  173. package/browser/addons/vrm/importer/vrm0_importer.py +1723 -0
  174. package/browser/addons/vrm/importer/vrm1_importer.py +2014 -0
  175. package/browser/addons/vrm/importer/vrm_animation_importer.py +763 -0
  176. package/browser/addons/vrm/importer/vrm_diff.py +316 -0
  177. package/browser/addons/vrm/locale/__init__.py +1 -0
  178. package/browser/addons/vrm/locale/ja_jp.py +826 -0
  179. package/browser/addons/vrm/locale/ruff.toml +8 -0
  180. package/browser/addons/vrm/locale/translation_dictionary.py +10 -0
  181. package/browser/addons/vrm/locale/zh_hans.py +928 -0
  182. package/browser/addons/vrm/registration.py +670 -0
  183. package/browser/addons/vrm.json +18 -0
  184. package/browser/blender-emscripten-engine.mts +60 -128
  185. package/browser/blender-engine.mts +20 -186
  186. package/browser/blender-wali-engine.mts +35 -81
  187. package/browser/describe-thrown.mts +3 -6
  188. package/browser/document-chunks.mts +4 -16
  189. package/browser/gltf_export.py +3161 -0
  190. package/browser/heap-growth.mts +23 -0
  191. package/browser/index.ts +2 -0
  192. package/browser/protocol.ts +119 -35
  193. package/browser/pull-job.mts +2 -10
  194. package/browser/rna.ts +190 -73
  195. package/browser/runtime.ts +197 -82
  196. package/browser/session-addons.mts +33 -0
  197. package/browser/session-frame.mts +5 -24
  198. package/browser/session.py +1338 -1427
  199. package/browser/three/attach-presenter.ts +11 -39
  200. package/browser/three/blender-base64.ts +1 -36
  201. package/browser/three/blender-corner-normals.ts +1 -23
  202. package/browser/three/blender-display-shaders.json +1 -1
  203. package/browser/three/blender-display-shaders.lut +0 -0
  204. package/browser/three/blender-display-transform.ts +2 -1
  205. package/browser/three/blender-frame-columns.ts +5 -13
  206. package/browser/three/blender-graph-material.ts +18 -3
  207. package/browser/three/blender-node-graph.ts +10 -34
  208. package/browser/three/blender-physical-material.ts +2 -31
  209. package/browser/three/blender-runtime-armature.ts +7 -60
  210. package/browser/three/blender-runtime-frame.ts +9 -23
  211. package/browser/three/blender-runtime-geometry.ts +22 -26
  212. package/browser/three/blender-runtime-lighting.ts +48 -178
  213. package/browser/three/blender-runtime-shadows.ts +187 -1
  214. package/browser/three/blender-runtime-skeleton.ts +53 -1
  215. package/browser/three/blender-runtime-view.ts +505 -283
  216. package/browser/three/blender-runtime-weights.ts +9 -72
  217. package/browser/three/blender-sky.ts +13 -33
  218. package/browser/three/blender-triangulate.ts +7 -35
  219. package/browser/three/blender-viewport-sampling.ts +235 -0
  220. package/browser/three/blender-workbench-material.ts +25 -29
  221. package/browser/three/blender-workbench-outline.ts +199 -0
  222. package/browser/three/blender-world-volume.ts +62 -10
  223. package/browser/three/presenter.ts +10 -26
  224. package/browser/three/release.ts +5 -21
  225. package/browser/three/sky-precompute-worker.ts +1 -18
  226. package/browser/three/sky-worker.ts +5 -23
  227. package/browser/three/world-field-sampler.ts +12 -26
  228. package/browser/volter_three.py +9 -105
  229. package/browser/worker.ts +156 -151
  230. package/package.json +5 -2
  231. package/wasm/BUNDLE.json +17 -17
  232. package/wasm/DEPENDENCY-LICENSES.txt +14 -0
  233. package/wasm/blender_browser.data.br +0 -0
  234. package/wasm/blender_browser.js +1 -1
  235. package/wasm/blender_browser.wasm.br +0 -0
  236. package/browser/frame-transfer.mts +0 -19
  237. package/browser/three/agx-base-srgb.lut +0 -0
  238. package/browser/three/agx-look-medium-high-contrast.lut +0 -0
  239. package/browser/three/agx-look-punchy.lut +0 -0
  240. package/browser/three/blender-agx.ts +0 -235
  241. package/browser/three/blender-display-lut.ts +0 -145
  242. package/browser/three/blender-filmic.ts +0 -49
  243. package/browser/three/blender-standard.ts +0 -68
  244. package/browser/three/blender-world-visibility.ts +0 -278
  245. 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]