@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,2087 @@
1
+ # SPDX-License-Identifier: MIT OR GPL-3.0-or-later
2
+ import math
3
+ import sys
4
+ from collections.abc import Mapping, Sequence
5
+ from sys import float_info
6
+ from typing import TYPE_CHECKING, ClassVar, Optional
7
+
8
+ import bpy
9
+ from bpy.app.translations import pgettext
10
+ from bpy.props import (
11
+ BoolProperty,
12
+ CollectionProperty,
13
+ EnumProperty,
14
+ FloatProperty,
15
+ FloatVectorProperty,
16
+ IntProperty,
17
+ PointerProperty,
18
+ StringProperty,
19
+ )
20
+ from bpy.types import (
21
+ Action,
22
+ Armature,
23
+ Context,
24
+ Image,
25
+ Material,
26
+ Mesh,
27
+ Object,
28
+ PropertyGroup,
29
+ )
30
+ from mathutils import Matrix, Quaternion, Vector
31
+
32
+ from ...common import convert, shader
33
+ from ...common.animation import defer_shape_key_update
34
+ from ...common.char import DISABLE_TRANSLATION
35
+ from ...common.logger import get_logger
36
+ from ...common.rotation import set_rotation_without_mode_change
37
+ from ...common.vrm1.expression_preset import (
38
+ ExpressionPreset,
39
+ ExpressionPresets,
40
+ )
41
+ from ...common.vrm1.human_bone import (
42
+ HumanBoneName,
43
+ HumanBoneSpecification,
44
+ HumanBoneSpecifications,
45
+ )
46
+ from ..extension_accessor import get_armature_extension, get_material_extension
47
+ from ..mtoon1.property_group import Mtoon1MatcapTextureInfoPropertyGroup
48
+ from ..property_group import (
49
+ HumanoidStructureBonePropertyGroup,
50
+ MaterialPropertyGroup,
51
+ MeshObjectPropertyGroup,
52
+ StringPropertyGroup,
53
+ clear_expression_material_binds,
54
+ property_group_enum,
55
+ )
56
+
57
+ if TYPE_CHECKING:
58
+ from ..property_group import CollectionPropertyProtocol
59
+
60
+
61
+ _logger = get_logger(__name__)
62
+
63
+
64
+ class Vrm1HumanBoneNodePropertyGroup(HumanoidStructureBonePropertyGroup):
65
+ def update_bone_name_candidates(
66
+ self,
67
+ armature_data: Armature,
68
+ target: HumanBoneSpecification,
69
+ bpy_bone_name_to_human_bone_specification: dict[str, HumanBoneSpecification],
70
+ error_bpy_bone_names: Sequence[str],
71
+ ) -> bool:
72
+ new_candidates = HumanoidStructureBonePropertyGroup.find_bone_candidates(
73
+ armature_data,
74
+ target,
75
+ bpy_bone_name_to_human_bone_specification,
76
+ error_bpy_bone_names,
77
+ )
78
+
79
+ bone_name_candidates = self.bone_name_candidates
80
+ if bone_name_candidates == new_candidates:
81
+ return False
82
+
83
+ bone_name_candidates.clear()
84
+ bone_name_candidates.update(new_candidates)
85
+ return True
86
+
87
+
88
+ # https://github.com/vrm-c/vrm-specification/blob/6fb6baaf9b9095a84fb82c8384db36e1afeb3558/specification/VRMC_vrm-1.0-beta/schema/VRMC_vrm.humanoid.humanBones.humanBone.schema.json
89
+ class Vrm1HumanBonePropertyGroup(PropertyGroup):
90
+ node: PointerProperty( # type: ignore[valid-type]
91
+ type=Vrm1HumanBoneNodePropertyGroup
92
+ )
93
+
94
+ if TYPE_CHECKING:
95
+ # This code is auto generated.
96
+ # To regenerate, run the `uv run tools/property_typing.py` command.
97
+ node: Vrm1HumanBoneNodePropertyGroup # type: ignore[no-redef]
98
+
99
+
100
+ # https://github.com/vrm-c/vrm-specification/blob/6fb6baaf9b9095a84fb82c8384db36e1afeb3558/specification/VRMC_vrm-1.0-beta/schema/VRMC_vrm.humanoid.humanBones.schema.json
101
+ class Vrm1HumanBonesPropertyGroup(PropertyGroup):
102
+ hips: PointerProperty( # type: ignore[valid-type]
103
+ type=Vrm1HumanBonePropertyGroup
104
+ )
105
+ spine: PointerProperty( # type: ignore[valid-type]
106
+ type=Vrm1HumanBonePropertyGroup
107
+ )
108
+ chest: PointerProperty( # type: ignore[valid-type]
109
+ type=Vrm1HumanBonePropertyGroup
110
+ )
111
+ upper_chest: PointerProperty( # type: ignore[valid-type]
112
+ type=Vrm1HumanBonePropertyGroup
113
+ )
114
+ neck: PointerProperty( # type: ignore[valid-type]
115
+ type=Vrm1HumanBonePropertyGroup
116
+ )
117
+ head: PointerProperty( # type: ignore[valid-type]
118
+ type=Vrm1HumanBonePropertyGroup
119
+ )
120
+ left_eye: PointerProperty( # type: ignore[valid-type]
121
+ type=Vrm1HumanBonePropertyGroup
122
+ )
123
+ right_eye: PointerProperty( # type: ignore[valid-type]
124
+ type=Vrm1HumanBonePropertyGroup
125
+ )
126
+ jaw: PointerProperty( # type: ignore[valid-type]
127
+ type=Vrm1HumanBonePropertyGroup
128
+ )
129
+ left_upper_leg: PointerProperty( # type: ignore[valid-type]
130
+ type=Vrm1HumanBonePropertyGroup
131
+ )
132
+ left_lower_leg: PointerProperty( # type: ignore[valid-type]
133
+ type=Vrm1HumanBonePropertyGroup
134
+ )
135
+ left_foot: PointerProperty( # type: ignore[valid-type]
136
+ type=Vrm1HumanBonePropertyGroup
137
+ )
138
+ left_toes: PointerProperty( # type: ignore[valid-type]
139
+ type=Vrm1HumanBonePropertyGroup
140
+ )
141
+ right_upper_leg: PointerProperty( # type: ignore[valid-type]
142
+ type=Vrm1HumanBonePropertyGroup
143
+ )
144
+ right_lower_leg: PointerProperty( # type: ignore[valid-type]
145
+ type=Vrm1HumanBonePropertyGroup
146
+ )
147
+ right_foot: PointerProperty( # type: ignore[valid-type]
148
+ type=Vrm1HumanBonePropertyGroup
149
+ )
150
+ right_toes: PointerProperty( # type: ignore[valid-type]
151
+ type=Vrm1HumanBonePropertyGroup
152
+ )
153
+ left_shoulder: PointerProperty( # type: ignore[valid-type]
154
+ type=Vrm1HumanBonePropertyGroup
155
+ )
156
+ left_upper_arm: PointerProperty( # type: ignore[valid-type]
157
+ type=Vrm1HumanBonePropertyGroup
158
+ )
159
+ left_lower_arm: PointerProperty( # type: ignore[valid-type]
160
+ type=Vrm1HumanBonePropertyGroup
161
+ )
162
+ left_hand: PointerProperty( # type: ignore[valid-type]
163
+ type=Vrm1HumanBonePropertyGroup
164
+ )
165
+ right_shoulder: PointerProperty( # type: ignore[valid-type]
166
+ type=Vrm1HumanBonePropertyGroup
167
+ )
168
+ right_upper_arm: PointerProperty( # type: ignore[valid-type]
169
+ type=Vrm1HumanBonePropertyGroup
170
+ )
171
+ right_lower_arm: PointerProperty( # type: ignore[valid-type]
172
+ type=Vrm1HumanBonePropertyGroup
173
+ )
174
+ right_hand: PointerProperty( # type: ignore[valid-type]
175
+ type=Vrm1HumanBonePropertyGroup
176
+ )
177
+ left_thumb_metacarpal: PointerProperty( # type: ignore[valid-type]
178
+ type=Vrm1HumanBonePropertyGroup
179
+ )
180
+ left_thumb_proximal: PointerProperty( # type: ignore[valid-type]
181
+ type=Vrm1HumanBonePropertyGroup
182
+ )
183
+ left_thumb_distal: PointerProperty( # type: ignore[valid-type]
184
+ type=Vrm1HumanBonePropertyGroup
185
+ )
186
+ left_index_proximal: PointerProperty( # type: ignore[valid-type]
187
+ type=Vrm1HumanBonePropertyGroup
188
+ )
189
+ left_index_intermediate: PointerProperty( # type: ignore[valid-type]
190
+ type=Vrm1HumanBonePropertyGroup
191
+ )
192
+ left_index_distal: PointerProperty( # type: ignore[valid-type]
193
+ type=Vrm1HumanBonePropertyGroup
194
+ )
195
+ left_middle_proximal: PointerProperty( # type: ignore[valid-type]
196
+ type=Vrm1HumanBonePropertyGroup
197
+ )
198
+ left_middle_intermediate: PointerProperty( # type: ignore[valid-type]
199
+ type=Vrm1HumanBonePropertyGroup
200
+ )
201
+ left_middle_distal: PointerProperty( # type: ignore[valid-type]
202
+ type=Vrm1HumanBonePropertyGroup
203
+ )
204
+ left_ring_proximal: PointerProperty( # type: ignore[valid-type]
205
+ type=Vrm1HumanBonePropertyGroup
206
+ )
207
+ left_ring_intermediate: PointerProperty( # type: ignore[valid-type]
208
+ type=Vrm1HumanBonePropertyGroup
209
+ )
210
+ left_ring_distal: PointerProperty( # type: ignore[valid-type]
211
+ type=Vrm1HumanBonePropertyGroup
212
+ )
213
+ left_little_proximal: PointerProperty( # type: ignore[valid-type]
214
+ type=Vrm1HumanBonePropertyGroup
215
+ )
216
+ left_little_intermediate: PointerProperty( # type: ignore[valid-type]
217
+ type=Vrm1HumanBonePropertyGroup
218
+ )
219
+ left_little_distal: PointerProperty( # type: ignore[valid-type]
220
+ type=Vrm1HumanBonePropertyGroup
221
+ )
222
+ right_thumb_metacarpal: PointerProperty( # type: ignore[valid-type]
223
+ type=Vrm1HumanBonePropertyGroup
224
+ )
225
+ right_thumb_proximal: PointerProperty( # type: ignore[valid-type]
226
+ type=Vrm1HumanBonePropertyGroup
227
+ )
228
+ right_thumb_distal: PointerProperty( # type: ignore[valid-type]
229
+ type=Vrm1HumanBonePropertyGroup
230
+ )
231
+ right_index_proximal: PointerProperty( # type: ignore[valid-type]
232
+ type=Vrm1HumanBonePropertyGroup
233
+ )
234
+ right_index_intermediate: PointerProperty( # type: ignore[valid-type]
235
+ type=Vrm1HumanBonePropertyGroup
236
+ )
237
+ right_index_distal: PointerProperty( # type: ignore[valid-type]
238
+ type=Vrm1HumanBonePropertyGroup
239
+ )
240
+ right_middle_proximal: PointerProperty( # type: ignore[valid-type]
241
+ type=Vrm1HumanBonePropertyGroup
242
+ )
243
+ right_middle_intermediate: PointerProperty( # type: ignore[valid-type]
244
+ type=Vrm1HumanBonePropertyGroup
245
+ )
246
+ right_middle_distal: PointerProperty( # type: ignore[valid-type]
247
+ type=Vrm1HumanBonePropertyGroup
248
+ )
249
+ right_ring_proximal: PointerProperty( # type: ignore[valid-type]
250
+ type=Vrm1HumanBonePropertyGroup
251
+ )
252
+ right_ring_intermediate: PointerProperty( # type: ignore[valid-type]
253
+ type=Vrm1HumanBonePropertyGroup
254
+ )
255
+ right_ring_distal: PointerProperty( # type: ignore[valid-type]
256
+ type=Vrm1HumanBonePropertyGroup
257
+ )
258
+ right_little_proximal: PointerProperty( # type: ignore[valid-type]
259
+ type=Vrm1HumanBonePropertyGroup
260
+ )
261
+ right_little_intermediate: PointerProperty( # type: ignore[valid-type]
262
+ type=Vrm1HumanBonePropertyGroup
263
+ )
264
+ right_little_distal: PointerProperty( # type: ignore[valid-type]
265
+ type=Vrm1HumanBonePropertyGroup
266
+ )
267
+
268
+ # for UI
269
+ pointer_to_last_bone_names_str: ClassVar[dict[int, str]] = {}
270
+ initial_automatic_bone_assignment: BoolProperty( # type: ignore[valid-type]
271
+ default=True
272
+ )
273
+
274
+ def _update_filter_by_human_bone_hierarchy(self, _context: Context) -> None:
275
+ if not isinstance(armature_data := self.id_data, Armature):
276
+ return
277
+ HumanoidStructureBonePropertyGroup.clear_bone_name_candidates_cache(
278
+ human_bone.node
279
+ for human_bone in self.human_bone_name_to_human_bone().values()
280
+ )
281
+ HumanoidStructureBonePropertyGroup.update_all_vrm1_bone_name_candidates(
282
+ armature_data
283
+ )
284
+
285
+ filter_by_human_bone_hierarchy: BoolProperty( # type: ignore[valid-type]
286
+ name="Filter by VRM Human Bone Hierarchy",
287
+ description="Restrict selectable bones by VRM humanoid hierarchy",
288
+ default=True,
289
+ update=_update_filter_by_human_bone_hierarchy,
290
+ )
291
+
292
+ allow_non_humanoid_rig: BoolProperty( # type: ignore[valid-type]
293
+ name="Allow Non-Humanoid Rig",
294
+ )
295
+
296
+ def human_bone_name_to_human_bone(
297
+ self,
298
+ ) -> dict[HumanBoneName, Vrm1HumanBonePropertyGroup]:
299
+ return {
300
+ HumanBoneName.HIPS: self.hips,
301
+ HumanBoneName.SPINE: self.spine,
302
+ HumanBoneName.CHEST: self.chest,
303
+ HumanBoneName.UPPER_CHEST: self.upper_chest,
304
+ HumanBoneName.NECK: self.neck,
305
+ HumanBoneName.HEAD: self.head,
306
+ HumanBoneName.LEFT_EYE: self.left_eye,
307
+ HumanBoneName.RIGHT_EYE: self.right_eye,
308
+ HumanBoneName.JAW: self.jaw,
309
+ HumanBoneName.LEFT_UPPER_LEG: self.left_upper_leg,
310
+ HumanBoneName.LEFT_LOWER_LEG: self.left_lower_leg,
311
+ HumanBoneName.LEFT_FOOT: self.left_foot,
312
+ HumanBoneName.LEFT_TOES: self.left_toes,
313
+ HumanBoneName.RIGHT_UPPER_LEG: self.right_upper_leg,
314
+ HumanBoneName.RIGHT_LOWER_LEG: self.right_lower_leg,
315
+ HumanBoneName.RIGHT_FOOT: self.right_foot,
316
+ HumanBoneName.RIGHT_TOES: self.right_toes,
317
+ HumanBoneName.LEFT_SHOULDER: self.left_shoulder,
318
+ HumanBoneName.LEFT_UPPER_ARM: self.left_upper_arm,
319
+ HumanBoneName.LEFT_LOWER_ARM: self.left_lower_arm,
320
+ HumanBoneName.LEFT_HAND: self.left_hand,
321
+ HumanBoneName.RIGHT_SHOULDER: self.right_shoulder,
322
+ HumanBoneName.RIGHT_UPPER_ARM: self.right_upper_arm,
323
+ HumanBoneName.RIGHT_LOWER_ARM: self.right_lower_arm,
324
+ HumanBoneName.RIGHT_HAND: self.right_hand,
325
+ HumanBoneName.LEFT_THUMB_METACARPAL: self.left_thumb_metacarpal,
326
+ HumanBoneName.LEFT_THUMB_PROXIMAL: self.left_thumb_proximal,
327
+ HumanBoneName.LEFT_THUMB_DISTAL: self.left_thumb_distal,
328
+ HumanBoneName.LEFT_INDEX_PROXIMAL: self.left_index_proximal,
329
+ HumanBoneName.LEFT_INDEX_INTERMEDIATE: self.left_index_intermediate,
330
+ HumanBoneName.LEFT_INDEX_DISTAL: self.left_index_distal,
331
+ HumanBoneName.LEFT_MIDDLE_PROXIMAL: self.left_middle_proximal,
332
+ HumanBoneName.LEFT_MIDDLE_INTERMEDIATE: self.left_middle_intermediate,
333
+ HumanBoneName.LEFT_MIDDLE_DISTAL: self.left_middle_distal,
334
+ HumanBoneName.LEFT_RING_PROXIMAL: self.left_ring_proximal,
335
+ HumanBoneName.LEFT_RING_INTERMEDIATE: self.left_ring_intermediate,
336
+ HumanBoneName.LEFT_RING_DISTAL: self.left_ring_distal,
337
+ HumanBoneName.LEFT_LITTLE_PROXIMAL: self.left_little_proximal,
338
+ HumanBoneName.LEFT_LITTLE_INTERMEDIATE: self.left_little_intermediate,
339
+ HumanBoneName.LEFT_LITTLE_DISTAL: self.left_little_distal,
340
+ HumanBoneName.RIGHT_THUMB_METACARPAL: self.right_thumb_metacarpal,
341
+ HumanBoneName.RIGHT_THUMB_PROXIMAL: self.right_thumb_proximal,
342
+ HumanBoneName.RIGHT_THUMB_DISTAL: self.right_thumb_distal,
343
+ HumanBoneName.RIGHT_INDEX_PROXIMAL: self.right_index_proximal,
344
+ HumanBoneName.RIGHT_INDEX_INTERMEDIATE: self.right_index_intermediate,
345
+ HumanBoneName.RIGHT_INDEX_DISTAL: self.right_index_distal,
346
+ HumanBoneName.RIGHT_MIDDLE_PROXIMAL: self.right_middle_proximal,
347
+ HumanBoneName.RIGHT_MIDDLE_INTERMEDIATE: self.right_middle_intermediate,
348
+ HumanBoneName.RIGHT_MIDDLE_DISTAL: self.right_middle_distal,
349
+ HumanBoneName.RIGHT_RING_PROXIMAL: self.right_ring_proximal,
350
+ HumanBoneName.RIGHT_RING_INTERMEDIATE: self.right_ring_intermediate,
351
+ HumanBoneName.RIGHT_RING_DISTAL: self.right_ring_distal,
352
+ HumanBoneName.RIGHT_LITTLE_PROXIMAL: self.right_little_proximal,
353
+ HumanBoneName.RIGHT_LITTLE_INTERMEDIATE: self.right_little_intermediate,
354
+ HumanBoneName.RIGHT_LITTLE_DISTAL: self.right_little_distal,
355
+ }
356
+
357
+ def error_messages(self) -> Sequence[str]:
358
+ if self.allow_non_humanoid_rig:
359
+ return []
360
+ messages = [
361
+ message for _, message in self.human_bone_duplication_error_messages()
362
+ ]
363
+
364
+ human_bone_name_to_human_bone = self.human_bone_name_to_human_bone()
365
+ for human_bone_name, human_bone in human_bone_name_to_human_bone.items():
366
+ specification = HumanBoneSpecifications.get(human_bone_name)
367
+ bone_name = human_bone.node.bone_name
368
+ if not bone_name:
369
+ if specification.requirement:
370
+ messages.append(
371
+ pgettext(
372
+ 'Please assign Required VRM Human Bone "{human_bone_name}".'
373
+ ).format(human_bone_name=specification.title)
374
+ )
375
+ continue
376
+ if (
377
+ self.filter_by_human_bone_hierarchy
378
+ and bone_name not in human_bone.node.bone_name_candidates
379
+ ):
380
+ messages.append(
381
+ pgettext(
382
+ 'Couldn\'t assign "{bone_name}" bone'
383
+ + ' to VRM Human Bone "{human_bone_name}". '
384
+ ).format(bone_name=bone_name, human_bone_name=specification.title)
385
+ )
386
+ continue
387
+ if not specification.parent_requirement:
388
+ continue
389
+ if not specification.parent_name:
390
+ _logger.error("No parent for '%s' in spec", human_bone_name)
391
+ continue
392
+ parent = human_bone_name_to_human_bone.get(specification.parent_name)
393
+ if not parent:
394
+ _logger.error("No parent for '%s' in dict", human_bone_name)
395
+ continue
396
+ parent_specification = specification.parent
397
+ if not parent_specification:
398
+ _logger.error("No parent specification for '%s'", human_bone_name)
399
+ continue
400
+ if not parent.node.bone_name:
401
+ messages.append(
402
+ pgettext(
403
+ 'Please assign "{parent_name}"'
404
+ + ' because "{name}" requires it as its child bone.'
405
+ ).format(
406
+ name=specification.title,
407
+ parent_name=parent_specification.title,
408
+ )
409
+ )
410
+
411
+ return messages
412
+
413
+ def bones_are_correctly_assigned(self) -> bool:
414
+ return len(self.error_messages()) == 0
415
+
416
+ def human_bone_duplication_error_messages(self) -> list[tuple[str, str]]:
417
+ messages: list[tuple[str, str]] = []
418
+ bone_name_to_human_bone_titles: dict[str, list[str]] = {}
419
+ for name, human_bone in self.human_bone_name_to_human_bone().items():
420
+ bone_name = human_bone.node.bone_name
421
+ if not bone_name:
422
+ continue
423
+
424
+ human_bone_titles = bone_name_to_human_bone_titles.get(bone_name)
425
+ if human_bone_titles is None:
426
+ human_bone_titles = []
427
+ bone_name_to_human_bone_titles[bone_name] = human_bone_titles
428
+ human_bone_titles.append(HumanBoneSpecifications.get(name).title)
429
+
430
+ for bone_name, human_bone_titles in bone_name_to_human_bone_titles.items():
431
+ if len(human_bone_titles) < 2:
432
+ continue
433
+ messages.append(
434
+ (
435
+ bone_name,
436
+ pgettext(
437
+ 'Bone "{bone_name}" is assigned to multiple VRM Human Bones:'
438
+ + " {human_bone_names}."
439
+ ).format(
440
+ bone_name=bone_name,
441
+ human_bone_names=", ".join(human_bone_titles),
442
+ ),
443
+ )
444
+ )
445
+ return messages
446
+
447
+ @staticmethod
448
+ def fixup_human_bones(obj: Object) -> None:
449
+ armature_data = obj.data
450
+ if obj.type != "ARMATURE" or not isinstance(armature_data, Armature):
451
+ return
452
+
453
+ human_bones = get_armature_extension(armature_data).vrm1.humanoid.human_bones
454
+
455
+ # If the same Blender bone is set in multiple bone maps, delete one of them
456
+ fixup = True
457
+ while fixup:
458
+ fixup = False
459
+ found_node_bone_names: list[str] = []
460
+ for human_bone in human_bones.human_bone_name_to_human_bone().values():
461
+ if not human_bone.node.bone_name:
462
+ continue
463
+ if human_bone.node.bone_name not in found_node_bone_names:
464
+ found_node_bone_names.append(human_bone.node.bone_name)
465
+ continue
466
+ human_bone.node.bone_name = ""
467
+ fixup = True
468
+ break
469
+
470
+ @staticmethod
471
+ def update_all_bone_name_candidates(
472
+ context: Context,
473
+ armature_data_name: str,
474
+ *,
475
+ force: bool = False,
476
+ ) -> None:
477
+ armature_data = context.blend_data.armatures.get(armature_data_name)
478
+ if not isinstance(armature_data, Armature):
479
+ return
480
+
481
+ ext = get_armature_extension(armature_data)
482
+ if not ext.is_vrm1():
483
+ return
484
+
485
+ bone_names_str = "\n".join(
486
+ sorted(
487
+ bone.name + "\n" + (parent.name if (parent := bone.parent) else "")
488
+ for bone in armature_data.bones.values()
489
+ )
490
+ )
491
+ human_bones = ext.vrm1.humanoid.human_bones
492
+ pointer_key = human_bones.as_pointer()
493
+ pointer_to_last_bone_names_str = human_bones.pointer_to_last_bone_names_str
494
+ last_bone_names_str = pointer_to_last_bone_names_str.get(pointer_key)
495
+ if not force and last_bone_names_str == bone_names_str:
496
+ return
497
+ pointer_to_last_bone_names_str[pointer_key] = bone_names_str
498
+
499
+ HumanoidStructureBonePropertyGroup.update_all_vrm1_bone_name_candidates(
500
+ armature_data
501
+ )
502
+
503
+ if TYPE_CHECKING:
504
+ # This code is auto generated.
505
+ # To regenerate, run the `uv run tools/property_typing.py` command.
506
+ hips: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
507
+ spine: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
508
+ chest: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
509
+ upper_chest: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
510
+ neck: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
511
+ head: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
512
+ left_eye: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
513
+ right_eye: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
514
+ jaw: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
515
+ left_upper_leg: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
516
+ left_lower_leg: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
517
+ left_foot: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
518
+ left_toes: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
519
+ right_upper_leg: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
520
+ right_lower_leg: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
521
+ right_foot: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
522
+ right_toes: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
523
+ left_shoulder: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
524
+ left_upper_arm: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
525
+ left_lower_arm: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
526
+ left_hand: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
527
+ right_shoulder: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
528
+ right_upper_arm: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
529
+ right_lower_arm: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
530
+ right_hand: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
531
+ left_thumb_metacarpal: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
532
+ left_thumb_proximal: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
533
+ left_thumb_distal: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
534
+ left_index_proximal: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
535
+ left_index_intermediate: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
536
+ left_index_distal: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
537
+ left_middle_proximal: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
538
+ left_middle_intermediate: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
539
+ left_middle_distal: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
540
+ left_ring_proximal: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
541
+ left_ring_intermediate: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
542
+ left_ring_distal: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
543
+ left_little_proximal: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
544
+ left_little_intermediate: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
545
+ left_little_distal: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
546
+ right_thumb_metacarpal: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
547
+ right_thumb_proximal: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
548
+ right_thumb_distal: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
549
+ right_index_proximal: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
550
+ right_index_intermediate: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
551
+ right_index_distal: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
552
+ right_middle_proximal: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
553
+ right_middle_intermediate: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
554
+ right_middle_distal: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
555
+ right_ring_proximal: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
556
+ right_ring_intermediate: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
557
+ right_ring_distal: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
558
+ right_little_proximal: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
559
+ right_little_intermediate: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
560
+ right_little_distal: Vrm1HumanBonePropertyGroup # type: ignore[no-redef]
561
+ initial_automatic_bone_assignment: bool # type: ignore[no-redef]
562
+ filter_by_human_bone_hierarchy: bool # type: ignore[no-redef]
563
+ allow_non_humanoid_rig: bool # type: ignore[no-redef]
564
+
565
+
566
+ # https://github.com/vrm-c/vrm-specification/blob/6fb6baaf9b9095a84fb82c8384db36e1afeb3558/specification/VRMC_vrm-1.0-beta/schema/VRMC_vrm.humanoid.schema.json
567
+ class Vrm1HumanoidPropertyGroup(PropertyGroup):
568
+ human_bones: PointerProperty(type=Vrm1HumanBonesPropertyGroup) # type: ignore[valid-type]
569
+
570
+ # for T-Pose
571
+ def _update_pose_library(self, _context: Context) -> None:
572
+ self.pose_marker_name = ""
573
+
574
+ (
575
+ pose_enum,
576
+ (
577
+ POSE_AUTO_POSE,
578
+ POSE_REST_POSITION_POSE,
579
+ POSE_CURRENT_POSE,
580
+ POSE_CUSTOM_POSE,
581
+ ),
582
+ ) = property_group_enum(
583
+ (
584
+ "autoPose",
585
+ "Auto",
586
+ "Auto Pose",
587
+ "ARMATURE_DATA",
588
+ 3,
589
+ ),
590
+ (
591
+ "restPositionPose",
592
+ "Rest Position",
593
+ "Rest Position Pose",
594
+ "ARMATURE_DATA",
595
+ 0,
596
+ ),
597
+ (
598
+ "currentPose",
599
+ "Current Pose",
600
+ "Current Pose",
601
+ "ARMATURE_DATA",
602
+ 1,
603
+ ),
604
+ (
605
+ "customPose",
606
+ "Custom Pose",
607
+ "Custom Pose",
608
+ "ARMATURE_DATA",
609
+ 2,
610
+ ),
611
+ )
612
+
613
+ pose: EnumProperty( # type: ignore[valid-type]
614
+ items=pose_enum.items(),
615
+ name="T-Pose",
616
+ description="T-Pose",
617
+ default=POSE_AUTO_POSE.identifier,
618
+ )
619
+
620
+ pose_library: PointerProperty( # type: ignore[valid-type]
621
+ type=Action,
622
+ update=_update_pose_library,
623
+ )
624
+ pose_marker_name: StringProperty() # type: ignore[valid-type]
625
+
626
+ if TYPE_CHECKING:
627
+ # This code is auto generated.
628
+ # To regenerate, run the `uv run tools/property_typing.py` command.
629
+ human_bones: Vrm1HumanBonesPropertyGroup # type: ignore[no-redef]
630
+ pose: str # type: ignore[no-redef]
631
+ pose_library: Optional[Action] # type: ignore[no-redef]
632
+ pose_marker_name: str # type: ignore[no-redef]
633
+
634
+
635
+ # https://github.com/vrm-c/vrm-specification/blob/6fb6baaf9b9095a84fb82c8384db36e1afeb3558/specification/VRMC_vrm-1.0-beta/schema/VRMC_vrm.lookAt.rangeMap.schema.json
636
+ class Vrm1LookAtRangeMapPropertyGroup(PropertyGroup):
637
+ input_max_value: FloatProperty( # type: ignore[valid-type]
638
+ name="Input Max Value",
639
+ min=0.0001, # https://github.com/pixiv/three-vrm/issues/1197#issuecomment-1498492002
640
+ default=90.0,
641
+ max=180.0,
642
+ )
643
+ output_scale: FloatProperty( # type: ignore[valid-type]
644
+ name="Output Scale",
645
+ default=10.0,
646
+ )
647
+
648
+ if TYPE_CHECKING:
649
+ # This code is auto generated.
650
+ # To regenerate, run the `uv run tools/property_typing.py` command.
651
+ input_max_value: float # type: ignore[no-redef]
652
+ output_scale: float # type: ignore[no-redef]
653
+
654
+
655
+ # https://github.com/vrm-c/vrm-specification/blob/6fb6baaf9b9095a84fb82c8384db36e1afeb3558/specification/VRMC_vrm-1.0-beta/schema/VRMC_vrm.lookAt.schema.json
656
+ class Vrm1LookAtPropertyGroup(PropertyGroup):
657
+ offset_from_head_bone: FloatVectorProperty( # type: ignore[valid-type]
658
+ name="Offset From Head Bone",
659
+ size=3,
660
+ subtype="TRANSLATION",
661
+ unit="LENGTH",
662
+ default=(0, 0, 0),
663
+ )
664
+
665
+ (
666
+ type_enum,
667
+ (
668
+ TYPE_BONE,
669
+ TYPE_EXPRESSION,
670
+ ),
671
+ ) = property_group_enum(
672
+ ("bone", "Bone", "Bone", "BONE_DATA", 0),
673
+ (
674
+ "expression",
675
+ "Expression" + DISABLE_TRANSLATION,
676
+ "Expression",
677
+ "SHAPEKEY_DATA",
678
+ 1,
679
+ ),
680
+ )
681
+
682
+ type: EnumProperty( # type: ignore[valid-type]
683
+ name="Type",
684
+ items=type_enum.items(),
685
+ )
686
+
687
+ range_map_horizontal_inner: PointerProperty( # type: ignore[valid-type]
688
+ type=Vrm1LookAtRangeMapPropertyGroup,
689
+ )
690
+ range_map_horizontal_outer: PointerProperty( # type: ignore[valid-type]
691
+ type=Vrm1LookAtRangeMapPropertyGroup,
692
+ )
693
+ range_map_vertical_down: PointerProperty( # type: ignore[valid-type]
694
+ type=Vrm1LookAtRangeMapPropertyGroup,
695
+ )
696
+ range_map_vertical_up: PointerProperty( # type: ignore[valid-type]
697
+ type=Vrm1LookAtRangeMapPropertyGroup,
698
+ )
699
+
700
+ enable_preview: BoolProperty( # type: ignore[valid-type]
701
+ name="Enable Preview",
702
+ )
703
+ preview_target_bpy_object: PointerProperty( # type: ignore[valid-type]
704
+ type=Object,
705
+ name="Preview Target",
706
+ )
707
+ previous_preview_matrix: FloatVectorProperty( # type: ignore[valid-type]
708
+ size=16,
709
+ )
710
+
711
+ @staticmethod
712
+ def update_all_previews(context: Context) -> None:
713
+ for armature_object in context.visible_objects:
714
+ if armature_object.type != "ARMATURE":
715
+ continue
716
+ armature_data = armature_object.data
717
+ if not isinstance(armature_data, Armature):
718
+ continue
719
+ vrm1 = get_armature_extension(armature_data).vrm1
720
+ look_at = vrm1.look_at
721
+ look_at.update_preview(context, armature_object, vrm1, check_only=False)
722
+
723
+ def update_preview(
724
+ self,
725
+ _context: Context,
726
+ armature_object: Object,
727
+ vrm1: "Vrm1PropertyGroup",
728
+ *,
729
+ check_only: bool,
730
+ ) -> (
731
+ # Returns True if a resource-intensive operation is executed according to
732
+ # SceneWatcher's criteria.
733
+ bool
734
+ ):
735
+ if not self.enable_preview:
736
+ return False
737
+
738
+ preview_target_bpy_object = self.preview_target_bpy_object
739
+ if not preview_target_bpy_object:
740
+ return False
741
+
742
+ head_bone_name = vrm1.humanoid.human_bones.head.node.bone_name
743
+ if not head_bone_name:
744
+ return False
745
+
746
+ head_pose_bone = armature_object.pose.bones.get(head_bone_name)
747
+ if not head_pose_bone:
748
+ return False
749
+
750
+ head_parent_pose_bone = head_pose_bone.parent
751
+ if not head_parent_pose_bone:
752
+ return False
753
+
754
+ to_translation = preview_target_bpy_object.matrix_world.to_translation()
755
+ preview_matrix = [
756
+ float(e)
757
+ for row in (
758
+ Matrix.Translation(-to_translation)
759
+ @ armature_object.matrix_world
760
+ @ head_pose_bone.matrix
761
+ )
762
+ for e in row
763
+ ]
764
+ if len(preview_matrix) == len(self.previous_preview_matrix) and all(
765
+ abs(a - b) < float_info.epsilon
766
+ for a, b in zip(preview_matrix, self.previous_preview_matrix)
767
+ ):
768
+ return False
769
+
770
+ if check_only:
771
+ return True
772
+
773
+ self.previous_preview_matrix = preview_matrix
774
+
775
+ # TODO: Honor t-pose action
776
+ rest_head_bone_matrix = (
777
+ armature_object.matrix_world
778
+ @ head_pose_bone.bone.convert_local_to_pose(
779
+ Matrix(),
780
+ head_pose_bone.bone.matrix_local,
781
+ )
782
+ )
783
+ rest_head_bone_inverted_rotation = (
784
+ rest_head_bone_matrix.to_quaternion().inverted()
785
+ )
786
+
787
+ head_bone_without_rotation_matrix = (
788
+ armature_object.matrix_world
789
+ @ head_pose_bone.bone.convert_local_to_pose(
790
+ Matrix(),
791
+ head_pose_bone.bone.matrix_local,
792
+ parent_matrix=head_parent_pose_bone.matrix,
793
+ parent_matrix_local=head_parent_pose_bone.bone.matrix_local,
794
+ )
795
+ )
796
+
797
+ local_target_translation = (
798
+ head_bone_without_rotation_matrix
799
+ ).inverted_safe() @ to_translation - Vector(self.offset_from_head_bone)
800
+ forward_vector = rest_head_bone_inverted_rotation @ Vector((0, -1, 0))
801
+ right_vector = rest_head_bone_inverted_rotation @ Vector((-1, 0, 0))
802
+ up_vector = rest_head_bone_inverted_rotation @ Vector((0, 0, 1))
803
+
804
+ # logger.warning(f"local_target_translation={dump(local_target_translation)}")
805
+ # logger.warning(f"forward={dump(forward_vector)}")
806
+ # logger.warning(f"right={dump(right_vector)}")
807
+ # logger.warning(f"up={dump(up_vector)}")
808
+
809
+ forward_length = local_target_translation.dot(forward_vector)
810
+ right_length = local_target_translation.dot(right_vector)
811
+ up_length = local_target_translation.dot(up_vector)
812
+
813
+ # https://github.com/vrm-c/vrm-specification/blob/0861a66eb2f2b76835322d775678047d616536b3/specification/VRMC_vrm-1.0/lookAt.md?plain=1#L180
814
+ if abs(forward_length) < float_info.epsilon:
815
+ return True
816
+
817
+ yaw_degrees = math.degrees(math.atan2(right_length, forward_length))
818
+ pitch_degrees = math.degrees(
819
+ math.atan2(
820
+ up_length,
821
+ math.sqrt(math.pow(right_length, 2) + math.pow(forward_length, 2)),
822
+ )
823
+ )
824
+ if vrm1.look_at.type == vrm1.look_at.TYPE_BONE.identifier:
825
+ self._apply_eye_bone_preview(
826
+ vrm1,
827
+ yaw_degrees,
828
+ pitch_degrees,
829
+ armature_object,
830
+ HumanBoneName.RIGHT_EYE,
831
+ )
832
+ self._apply_eye_bone_preview(
833
+ vrm1,
834
+ yaw_degrees,
835
+ pitch_degrees,
836
+ armature_object,
837
+ HumanBoneName.LEFT_EYE,
838
+ )
839
+ elif vrm1.look_at.type == vrm1.look_at.TYPE_EXPRESSION.identifier:
840
+ self._apply_expression_preview(vrm1, yaw_degrees, pitch_degrees)
841
+
842
+ return True
843
+
844
+ # https://github.com/vrm-c/vrm-specification/blob/0861a66eb2f2b76835322d775678047d616536b3/specification/VRMC_vrm-1.0/lookAt.md?plain=1#L230
845
+ def _apply_eye_bone_preview(
846
+ self,
847
+ vrm1: "Vrm1PropertyGroup",
848
+ yaw_degrees: float,
849
+ pitch_degrees: float,
850
+ armature_object: Object,
851
+ human_bone_name: HumanBoneName,
852
+ ) -> None:
853
+ if human_bone_name == HumanBoneName.RIGHT_EYE:
854
+ range_map_horizontal_right = vrm1.look_at.range_map_horizontal_outer
855
+ range_map_horizontal_left = vrm1.look_at.range_map_horizontal_inner
856
+ pose_bone = armature_object.pose.bones.get(
857
+ vrm1.humanoid.human_bones.right_eye.node.bone_name
858
+ )
859
+ elif human_bone_name == HumanBoneName.LEFT_EYE:
860
+ range_map_horizontal_right = vrm1.look_at.range_map_horizontal_inner
861
+ range_map_horizontal_left = vrm1.look_at.range_map_horizontal_outer
862
+ pose_bone = armature_object.pose.bones.get(
863
+ vrm1.humanoid.human_bones.left_eye.node.bone_name
864
+ )
865
+ else:
866
+ return
867
+
868
+ range_map_vertical_up = vrm1.look_at.range_map_vertical_up
869
+ range_map_vertical_down = vrm1.look_at.range_map_vertical_down
870
+
871
+ if not pose_bone:
872
+ return
873
+
874
+ parent_pose_bone = pose_bone.parent
875
+ if not parent_pose_bone:
876
+ return
877
+
878
+ if yaw_degrees < 0:
879
+ yaw_degrees = (
880
+ -min(abs(yaw_degrees), range_map_horizontal_left.input_max_value)
881
+ / range_map_horizontal_left.input_max_value
882
+ * range_map_horizontal_left.output_scale
883
+ )
884
+ else:
885
+ yaw_degrees = (
886
+ min(abs(yaw_degrees), range_map_horizontal_right.input_max_value)
887
+ / range_map_horizontal_right.input_max_value
888
+ * range_map_horizontal_right.output_scale
889
+ )
890
+
891
+ if pitch_degrees < 0:
892
+ # down
893
+ pitch_degrees = (
894
+ -min(abs(pitch_degrees), range_map_vertical_down.input_max_value)
895
+ / range_map_vertical_down.input_max_value
896
+ * range_map_vertical_down.output_scale
897
+ )
898
+ else:
899
+ # up
900
+ pitch_degrees = (
901
+ min(abs(pitch_degrees), range_map_vertical_up.input_max_value)
902
+ / range_map_vertical_up.input_max_value
903
+ * range_map_vertical_up.output_scale
904
+ )
905
+
906
+ # TODO: Honor t-pose action
907
+ rest_bone_matrix = (
908
+ armature_object.matrix_world
909
+ @ pose_bone.bone.convert_local_to_pose(
910
+ Matrix(),
911
+ pose_bone.bone.matrix_local,
912
+ )
913
+ )
914
+ reset_bone_inverted_rotation = rest_bone_matrix.to_quaternion().inverted()
915
+
916
+ forward_vector = reset_bone_inverted_rotation @ Vector((0, -1, 0))
917
+ right_vector = reset_bone_inverted_rotation @ Vector((-1, 0, 0))
918
+ up_vector = reset_bone_inverted_rotation @ Vector((0, 0, 1))
919
+
920
+ # logger.warning(f"{yaw_degrees=} {pitch_degrees=}")
921
+ # logger.warning(f"forward={dump(forward_vector)}")
922
+ # logger.warning(f"right={dump(right_vector)}")
923
+ # logger.warning(f"up={dump(up_vector)}")
924
+
925
+ rotation = Quaternion(
926
+ forward_vector.cross(right_vector), math.radians(yaw_degrees)
927
+ ) @ Quaternion(forward_vector.cross(up_vector), math.radians(pitch_degrees))
928
+ # logger.warning(f"rotation={dump(rotation)}")
929
+
930
+ set_rotation_without_mode_change(pose_bone, rotation)
931
+
932
+ # https://github.com/vrm-c/vrm-specification/blob/0861a66eb2f2b76835322d775678047d616536b3/specification/VRMC_vrm-1.0/lookAt.md?plain=1#L258
933
+ def _apply_expression_preview(
934
+ self,
935
+ vrm1: "Vrm1PropertyGroup",
936
+ yaw_degrees: float,
937
+ pitch_degrees: float,
938
+ ) -> None:
939
+ range_map_horizontal_outer = vrm1.look_at.range_map_horizontal_outer
940
+ range_map_vertical_up = vrm1.look_at.range_map_vertical_up
941
+ range_map_vertical_down = vrm1.look_at.range_map_vertical_down
942
+ look_left = vrm1.expressions.preset.look_left
943
+ look_right = vrm1.expressions.preset.look_right
944
+ look_up = vrm1.expressions.preset.look_up
945
+ look_down = vrm1.expressions.preset.look_down
946
+ # horizontal
947
+ if yaw_degrees < 0:
948
+ # left
949
+ yaw_weight = (
950
+ min(abs(yaw_degrees), range_map_horizontal_outer.input_max_value)
951
+ / range_map_horizontal_outer.input_max_value
952
+ * range_map_horizontal_outer.output_scale
953
+ )
954
+ look_left.preview = yaw_weight
955
+ look_right.preview = 0
956
+ else:
957
+ # right
958
+ yaw_weight = (
959
+ min(yaw_degrees, range_map_horizontal_outer.input_max_value)
960
+ / range_map_horizontal_outer.input_max_value
961
+ * range_map_horizontal_outer.output_scale
962
+ )
963
+ look_left.preview = 0
964
+ look_right.preview = yaw_weight
965
+
966
+ # vertical
967
+ if pitch_degrees < 0:
968
+ # down
969
+ pitch_weight = (
970
+ min(abs(pitch_degrees), range_map_vertical_down.input_max_value)
971
+ / range_map_vertical_down.input_max_value
972
+ * range_map_vertical_down.output_scale
973
+ )
974
+ look_down.preview = pitch_weight
975
+ look_up.preview = 0
976
+ else:
977
+ # up
978
+ pitch_weight = (
979
+ min(pitch_degrees, range_map_vertical_up.input_max_value)
980
+ / range_map_vertical_up.input_max_value
981
+ * range_map_vertical_up.output_scale
982
+ )
983
+ look_down.preview = 0
984
+ look_up.preview = pitch_weight
985
+
986
+ if TYPE_CHECKING:
987
+ # This code is auto generated.
988
+ # To regenerate, run the `uv run tools/property_typing.py` command.
989
+ offset_from_head_bone: Sequence[float] # type: ignore[no-redef]
990
+ type: str # type: ignore[no-redef]
991
+ range_map_horizontal_inner: ( # type: ignore[no-redef]
992
+ Vrm1LookAtRangeMapPropertyGroup
993
+ )
994
+ range_map_horizontal_outer: ( # type: ignore[no-redef]
995
+ Vrm1LookAtRangeMapPropertyGroup
996
+ )
997
+ range_map_vertical_down: ( # type: ignore[no-redef]
998
+ Vrm1LookAtRangeMapPropertyGroup
999
+ )
1000
+ range_map_vertical_up: Vrm1LookAtRangeMapPropertyGroup # type: ignore[no-redef]
1001
+ enable_preview: bool # type: ignore[no-redef]
1002
+ preview_target_bpy_object: Optional[Object] # type: ignore[no-redef]
1003
+ previous_preview_matrix: Sequence[float] # type: ignore[no-redef]
1004
+
1005
+
1006
+ # https://github.com/vrm-c/vrm-specification/blob/6fb6baaf9b9095a84fb82c8384db36e1afeb3558/specification/VRMC_vrm-1.0-beta/schema/VRMC_vrm.firstPerson.meshAnnotation.schema.json
1007
+ class Vrm1MeshAnnotationPropertyGroup(PropertyGroup):
1008
+ node: PointerProperty( # type: ignore[valid-type]
1009
+ type=MeshObjectPropertyGroup
1010
+ )
1011
+ type_enum, __types = property_group_enum(
1012
+ ("auto", "Auto", "", "NONE", 0),
1013
+ ("both", "Both", "", "NONE", 1),
1014
+ ("thirdPersonOnly", "Third-Person Only", "", "NONE", 2),
1015
+ ("firstPersonOnly", "First-Person Only", "", "NONE", 3),
1016
+ )
1017
+ type: EnumProperty( # type: ignore[valid-type]
1018
+ items=type_enum.items(),
1019
+ name="First Person Type",
1020
+ )
1021
+
1022
+ if TYPE_CHECKING:
1023
+ # This code is auto generated.
1024
+ # To regenerate, run the `uv run tools/property_typing.py` command.
1025
+ node: MeshObjectPropertyGroup # type: ignore[no-redef]
1026
+ type: str # type: ignore[no-redef]
1027
+
1028
+
1029
+ # https://github.com/vrm-c/vrm-specification/blob/6fb6baaf9b9095a84fb82c8384db36e1afeb3558/specification/VRMC_vrm-1.0-beta/schema/VRMC_vrm.firstPerson.schema.json
1030
+ class Vrm1FirstPersonPropertyGroup(PropertyGroup):
1031
+ mesh_annotations: CollectionProperty( # type: ignore[valid-type]
1032
+ name="Mesh Annotations",
1033
+ type=Vrm1MeshAnnotationPropertyGroup,
1034
+ )
1035
+
1036
+ # for UI
1037
+ active_mesh_annotation_index: IntProperty(min=0) # type: ignore[valid-type]
1038
+
1039
+ if TYPE_CHECKING:
1040
+ # This code is auto generated.
1041
+ # To regenerate, run the `uv run tools/property_typing.py` command.
1042
+ mesh_annotations: CollectionPropertyProtocol[ # type: ignore[no-redef]
1043
+ Vrm1MeshAnnotationPropertyGroup
1044
+ ]
1045
+ active_mesh_annotation_index: int # type: ignore[no-redef]
1046
+
1047
+
1048
+ def reset_expression_material_binds(context: Context) -> None:
1049
+ clear_expression_material_binds(context)
1050
+ for armature_data in context.blend_data.armatures:
1051
+ Vrm1ExpressionPropertyGroup.apply_previews(context, armature_data)
1052
+
1053
+
1054
+ # https://github.com/vrm-c/vrm-specification/blob/6fb6baaf9b9095a84fb82c8384db36e1afeb3558/specification/VRMC_vrm-1.0-beta/schema/VRMC_vrm.expressions.expression.morphTargetBind.schema.json
1055
+ class Vrm1MorphTargetBindPropertyGroup(PropertyGroup):
1056
+ def _update_preview(self, context: Context) -> None:
1057
+ if not isinstance(armature_data := self.id_data, Armature):
1058
+ return
1059
+ Vrm1ExpressionPropertyGroup.apply_previews(context, armature_data)
1060
+
1061
+ node: PointerProperty( # type: ignore[valid-type]
1062
+ type=MeshObjectPropertyGroup,
1063
+ update=_update_preview,
1064
+ )
1065
+ index: StringProperty( # type: ignore[valid-type]
1066
+ update=_update_preview,
1067
+ )
1068
+ weight: FloatProperty( # type: ignore[valid-type]
1069
+ min=0,
1070
+ default=1,
1071
+ max=1,
1072
+ update=_update_preview,
1073
+ )
1074
+
1075
+ if TYPE_CHECKING:
1076
+ # This code is auto generated.
1077
+ # To regenerate, run the `uv run tools/property_typing.py` command.
1078
+ node: MeshObjectPropertyGroup # type: ignore[no-redef]
1079
+ index: str # type: ignore[no-redef]
1080
+ weight: float # type: ignore[no-redef]
1081
+
1082
+
1083
+ # https://github.com/vrm-c/vrm-specification/blob/6fb6baaf9b9095a84fb82c8384db36e1afeb3558/specification/VRMC_vrm-1.0-beta/schema/VRMC_vrm.expressions.expression.materialColorBind.schema.json
1084
+ class Vrm1MaterialColorBindPropertyGroup(PropertyGroup):
1085
+ material: PointerProperty( # type: ignore[valid-type]
1086
+ name="Material",
1087
+ type=Material,
1088
+ update=lambda _, context: reset_expression_material_binds(context),
1089
+ )
1090
+
1091
+ (
1092
+ type_enum,
1093
+ (
1094
+ TYPE_COLOR,
1095
+ TYPE_EMISSION_COLOR,
1096
+ TYPE_SHADE_COLOR,
1097
+ TYPE_MATCAP_COLOR,
1098
+ TYPE_RIM_COLOR,
1099
+ TYPE_OUTLINE_COLOR,
1100
+ ),
1101
+ ) = property_group_enum(
1102
+ ("color", "Color", "", "NONE", 0),
1103
+ ("emissionColor", "Emission Color", "", "NONE", 1),
1104
+ ("shadeColor", "Shade Color", "", "NONE", 2),
1105
+ ("matcapColor", "Matcap Color", "", "NONE", 5),
1106
+ ("rimColor", "Rim Color", "", "NONE", 3),
1107
+ ("outlineColor", "Outline Color", "", "NONE", 4),
1108
+ )
1109
+ type: EnumProperty( # type: ignore[valid-type]
1110
+ name="Type",
1111
+ items=type_enum.items(),
1112
+ update=lambda _, context: reset_expression_material_binds(context),
1113
+ )
1114
+ target_value: FloatVectorProperty( # type: ignore[valid-type]
1115
+ name="Target Value",
1116
+ size=4,
1117
+ subtype="COLOR",
1118
+ min=0,
1119
+ soft_max=1,
1120
+ )
1121
+
1122
+ def _get_target_value_as_rgb(self) -> tuple[float, float, float]:
1123
+ return (
1124
+ self.target_value[0],
1125
+ self.target_value[1],
1126
+ self.target_value[2],
1127
+ )
1128
+
1129
+ def _set_target_value_as_rgb(self, value: Sequence[float]) -> None:
1130
+ if len(value) < 3:
1131
+ return
1132
+ self.target_value = (
1133
+ value[0],
1134
+ value[1],
1135
+ value[2],
1136
+ self.target_value[3],
1137
+ )
1138
+
1139
+ target_value_as_rgb: FloatVectorProperty( # type: ignore[valid-type]
1140
+ name="Target Value",
1141
+ size=3,
1142
+ subtype="COLOR",
1143
+ get=_get_target_value_as_rgb,
1144
+ set=_set_target_value_as_rgb,
1145
+ )
1146
+
1147
+ if TYPE_CHECKING:
1148
+ # This code is auto generated.
1149
+ # To regenerate, run the `uv run tools/property_typing.py` command.
1150
+ material: Optional[Material] # type: ignore[no-redef]
1151
+ type: str # type: ignore[no-redef]
1152
+ target_value: Sequence[float] # type: ignore[no-redef]
1153
+ target_value_as_rgb: Sequence[float] # type: ignore[no-redef]
1154
+
1155
+
1156
+ # https://github.com/vrm-c/vrm-specification/blob/6fb6baaf9b9095a84fb82c8384db36e1afeb3558/specification/VRMC_vrm-1.0-beta/schema/VRMC_vrm.expressions.expression.textureTransformBind.schema.json
1157
+ class Vrm1TextureTransformBindPropertyGroup(PropertyGroup):
1158
+ material: PointerProperty( # type: ignore[valid-type]
1159
+ name="Material",
1160
+ type=Material,
1161
+ update=lambda _, context: reset_expression_material_binds(context),
1162
+ )
1163
+ scale: FloatVectorProperty( # type: ignore[valid-type]
1164
+ size=2,
1165
+ default=(1, 1),
1166
+ )
1167
+ offset: FloatVectorProperty( # type: ignore[valid-type]
1168
+ size=2,
1169
+ default=(0, 0),
1170
+ )
1171
+ show_experimental_preview_feature: BoolProperty( # type: ignore[valid-type]
1172
+ name="[Experimental] Show Preview Feature"
1173
+ )
1174
+
1175
+ if TYPE_CHECKING:
1176
+ # This code is auto generated.
1177
+ # To regenerate, run the `uv run tools/property_typing.py` command.
1178
+ material: Optional[Material] # type: ignore[no-redef]
1179
+ scale: Sequence[float] # type: ignore[no-redef]
1180
+ offset: Sequence[float] # type: ignore[no-redef]
1181
+ show_experimental_preview_feature: bool # type: ignore[no-redef]
1182
+
1183
+
1184
+ # https://github.com/vrm-c/vrm-specification/blob/6fb6baaf9b9095a84fb82c8384db36e1afeb3558/specification/VRMC_vrm-1.0-beta/schema/VRMC_vrm.expressions.expression.schema.json
1185
+ class Vrm1ExpressionPropertyGroup(PropertyGroup):
1186
+ morph_target_binds: CollectionProperty( # type: ignore[valid-type]
1187
+ type=Vrm1MorphTargetBindPropertyGroup
1188
+ )
1189
+ material_color_binds: CollectionProperty( # type: ignore[valid-type]
1190
+ type=Vrm1MaterialColorBindPropertyGroup
1191
+ )
1192
+ texture_transform_binds: CollectionProperty( # type: ignore[valid-type]
1193
+ type=Vrm1TextureTransformBindPropertyGroup
1194
+ )
1195
+
1196
+ materials_to_update: CollectionProperty( # type: ignore[valid-type]
1197
+ type=MaterialPropertyGroup, options={"HIDDEN"}
1198
+ )
1199
+
1200
+ expression_override_type_enum, __expression_override_types = property_group_enum(
1201
+ ("none", "None", "", "NONE", 0),
1202
+ ("block", "Block", "", "NONE", 1),
1203
+ ("blend", "Blend", "", "NONE", 2),
1204
+ )
1205
+
1206
+ pending_preview_update_armature_data_names: ClassVar[list[str]] = []
1207
+
1208
+ def _update_preview(self, context: Context) -> None:
1209
+ if not self.name:
1210
+ _logger.debug("Unnamed expression: %s", type(self))
1211
+ return
1212
+
1213
+ armature_data = self.id_data
1214
+ if not isinstance(armature_data, Armature):
1215
+ # This is getting triggered after importing VRMA files
1216
+ _logger.error("No armature for %s", self.name)
1217
+ return
1218
+
1219
+ if defer_shape_key_update(context):
1220
+ if (
1221
+ armature_data.name
1222
+ not in self.pending_preview_update_armature_data_names
1223
+ ):
1224
+ self.pending_preview_update_armature_data_names.append(
1225
+ armature_data.name
1226
+ )
1227
+ return
1228
+
1229
+ self.apply_previews(context, armature_data)
1230
+
1231
+ @classmethod
1232
+ def apply_pending_preview_update_to_armatures(cls, context: Context) -> None:
1233
+ for armature_data_name in cls.pending_preview_update_armature_data_names:
1234
+ armature_data = context.blend_data.armatures.get(armature_data_name)
1235
+ if not armature_data:
1236
+ continue
1237
+ cls.apply_previews(context, armature_data)
1238
+ cls.pending_preview_update_armature_data_names.clear()
1239
+
1240
+ @classmethod
1241
+ def apply_previews(cls, context: Context, armature_data: Armature) -> None:
1242
+ expressions = get_armature_extension(armature_data).vrm1.expressions
1243
+ name_to_expression_dict = expressions.all_name_to_expression_dict()
1244
+
1245
+ mouth_block_rate = 0.0
1246
+ blink_block_rate = 0.0
1247
+ look_at_block_rate = 0.0
1248
+ for expression in name_to_expression_dict.values():
1249
+ if expression.preview < float_info.epsilon:
1250
+ continue
1251
+
1252
+ if expression.override_mouth == "blend":
1253
+ mouth_block_rate += expression.preview
1254
+ if expression.override_blink == "blend":
1255
+ blink_block_rate += expression.preview
1256
+ if expression.override_look_at == "blend":
1257
+ look_at_block_rate += expression.preview
1258
+
1259
+ if expression.override_mouth == "block":
1260
+ mouth_block_rate = 1.0
1261
+ if expression.override_blink == "block":
1262
+ blink_block_rate = 1.0
1263
+ if expression.override_look_at == "block":
1264
+ look_at_block_rate = 1.0
1265
+
1266
+ mouth_blend_factor = max(0.0, min(1 - mouth_block_rate, 1.0))
1267
+ blink_blend_factor = max(0.0, min(1 - blink_block_rate, 1.0))
1268
+ look_at_blend_factor = max(0.0, min(1 - look_at_block_rate, 1.0))
1269
+
1270
+ mesh_object_name_to_key_block_name_to_value: dict[str, dict[str, float]] = {}
1271
+ material_name_to_color_type_to_vector: dict[str, dict[str, Vector]] = {}
1272
+ material_name_to_uv_transform: dict[str, tuple[float, Vector, Vector]] = {}
1273
+ for name, expression in name_to_expression_dict.items():
1274
+ if expression.is_binary:
1275
+ # https://github.com/vrm-c/vrm-specification/blob/5b1071b9da1d60bb1bab0b70754615127fc43e9e/specification/VRMC_vrm-1.0/expressions.md?plain=1#L46
1276
+ preview = 1.0 if expression.preview > 0.5 else 0.0
1277
+ else:
1278
+ preview = expression.preview
1279
+
1280
+ if expressions.preset.is_mouth_expression(name):
1281
+ preview *= mouth_blend_factor
1282
+ elif expressions.preset.is_blink_expression(name):
1283
+ preview *= blink_blend_factor
1284
+ elif expressions.preset.is_look_at_expression(name):
1285
+ preview *= look_at_blend_factor
1286
+
1287
+ for texture_transform_bind in expression.texture_transform_binds:
1288
+ material = texture_transform_bind.material
1289
+ if not material:
1290
+ continue
1291
+ mtoon1 = get_material_extension(material).mtoon1
1292
+ if not mtoon1.enabled:
1293
+ continue
1294
+ uv_transform: Optional[tuple[float, Vector, Vector]] = (
1295
+ material_name_to_uv_transform.get(material.name)
1296
+ )
1297
+ if not uv_transform:
1298
+ uv_transform = (
1299
+ 0,
1300
+ Vector((0, 0)),
1301
+ Vector((0, 0)),
1302
+ )
1303
+ material_name_to_uv_transform[material.name] = (
1304
+ uv_transform[0] + preview,
1305
+ uv_transform[1] + Vector(texture_transform_bind.scale) * preview,
1306
+ uv_transform[2] + Vector(texture_transform_bind.offset) * preview,
1307
+ )
1308
+
1309
+ for material_color_bind in expression.material_color_binds:
1310
+ material = material_color_bind.material
1311
+ if not material:
1312
+ continue
1313
+ mtoon1 = get_material_extension(material).mtoon1
1314
+ if not mtoon1.enabled:
1315
+ continue
1316
+ if (
1317
+ material_color_bind.type
1318
+ == Vrm1MaterialColorBindPropertyGroup.TYPE_COLOR.identifier
1319
+ ):
1320
+ target_vector = Vector(material_color_bind.target_value)
1321
+ default_vector = Vector(
1322
+ mtoon1.pbr_metallic_roughness.base_color_factor
1323
+ )
1324
+ elif (
1325
+ material_color_bind.type
1326
+ == Vrm1MaterialColorBindPropertyGroup.TYPE_EMISSION_COLOR.identifier
1327
+ ):
1328
+ target_vector = Vector(material_color_bind.target_value_as_rgb)
1329
+ default_vector = Vector(mtoon1.emissive_factor)
1330
+ elif (
1331
+ material_color_bind.type
1332
+ == Vrm1MaterialColorBindPropertyGroup.TYPE_SHADE_COLOR.identifier
1333
+ ):
1334
+ target_vector = Vector(material_color_bind.target_value_as_rgb)
1335
+ default_vector = Vector(
1336
+ mtoon1.extensions.vrmc_materials_mtoon.shade_color_factor
1337
+ )
1338
+ elif (
1339
+ material_color_bind.type
1340
+ == Vrm1MaterialColorBindPropertyGroup.TYPE_MATCAP_COLOR.identifier
1341
+ ):
1342
+ target_vector = Vector(material_color_bind.target_value_as_rgb)
1343
+ default_vector = Vector(
1344
+ mtoon1.extensions.vrmc_materials_mtoon.matcap_factor
1345
+ )
1346
+ elif (
1347
+ material_color_bind.type
1348
+ == Vrm1MaterialColorBindPropertyGroup.TYPE_RIM_COLOR.identifier
1349
+ ):
1350
+ target_vector = Vector(material_color_bind.target_value_as_rgb)
1351
+ default_vector = Vector(
1352
+ mtoon1.extensions.vrmc_materials_mtoon.parametric_rim_color_factor
1353
+ )
1354
+ elif (
1355
+ material_color_bind.type
1356
+ == Vrm1MaterialColorBindPropertyGroup.TYPE_OUTLINE_COLOR.identifier
1357
+ ):
1358
+ target_vector = Vector(material_color_bind.target_value_as_rgb)
1359
+ default_vector = Vector(
1360
+ mtoon1.extensions.vrmc_materials_mtoon.outline_color_factor
1361
+ )
1362
+ else:
1363
+ continue
1364
+
1365
+ color_type_to_vector = material_name_to_color_type_to_vector.get(
1366
+ material.name
1367
+ )
1368
+ if not color_type_to_vector:
1369
+ color_type_to_vector = {}
1370
+ material_name_to_color_type_to_vector[material.name] = (
1371
+ color_type_to_vector
1372
+ )
1373
+
1374
+ current_vector = color_type_to_vector.get(material_color_bind.type)
1375
+ if current_vector is None:
1376
+ current_vector = (target_vector - default_vector) * preview
1377
+ else:
1378
+ current_vector = (
1379
+ current_vector + (target_vector - default_vector) * preview
1380
+ )
1381
+ color_type_to_vector[material_color_bind.type] = current_vector
1382
+
1383
+ for morph_target_bind in expression.morph_target_binds:
1384
+ value = morph_target_bind.weight * preview
1385
+
1386
+ mesh_object_name = morph_target_bind.node.mesh_object_name
1387
+ key_block_name = morph_target_bind.index
1388
+ key_block_name_to_value = (
1389
+ mesh_object_name_to_key_block_name_to_value.get(mesh_object_name)
1390
+ )
1391
+ if not key_block_name_to_value:
1392
+ mesh_object_name_to_key_block_name_to_value[mesh_object_name] = {
1393
+ key_block_name: value
1394
+ }
1395
+ continue
1396
+ key_block_name_to_value[key_block_name] = (
1397
+ key_block_name_to_value.get(key_block_name, 0) + value
1398
+ )
1399
+
1400
+ for material_name, (
1401
+ total_preview,
1402
+ scale,
1403
+ offset,
1404
+ ) in material_name_to_uv_transform.items():
1405
+ material = context.blend_data.materials.get(material_name)
1406
+ if not material:
1407
+ continue
1408
+ mtoon1 = get_material_extension(material).mtoon1
1409
+ for texture_info in mtoon1.all_texture_info():
1410
+ if isinstance(texture_info, Mtoon1MatcapTextureInfoPropertyGroup):
1411
+ continue
1412
+ khr_texture_transform = texture_info.extensions.khr_texture_transform
1413
+ khr_texture_transform.set_texture_uv(
1414
+ shader.UV_GROUP_EXPRESSION_UV_OFFSET_BIND_LABEL,
1415
+ offset - Vector(khr_texture_transform.offset) * total_preview,
1416
+ )
1417
+ khr_texture_transform.set_texture_uv(
1418
+ shader.UV_GROUP_EXPRESSION_UV_SCALE_BIND_LABEL,
1419
+ scale - Vector(khr_texture_transform.scale) * total_preview,
1420
+ )
1421
+
1422
+ for (
1423
+ material_name,
1424
+ color_type_to_vector,
1425
+ ) in material_name_to_color_type_to_vector.items():
1426
+ material = context.blend_data.materials.get(material_name)
1427
+ if not material:
1428
+ continue
1429
+ mtoon1 = get_material_extension(material).mtoon1
1430
+ for material_color_bind_type, vector in color_type_to_vector.items():
1431
+ if (
1432
+ material_color_bind_type
1433
+ == Vrm1MaterialColorBindPropertyGroup.TYPE_COLOR.identifier
1434
+ ):
1435
+ mtoon1.set_vector3(
1436
+ shader.OUTPUT_GROUP_NAME,
1437
+ shader.OUTPUT_GROUP_EXPRESSION_COLOR_BIND_LABEL,
1438
+ vector.xyz,
1439
+ )
1440
+ mtoon1.set_value(
1441
+ shader.OUTPUT_GROUP_NAME,
1442
+ shader.OUTPUT_GROUP_EXPRESSION_COLOR_ALPHA_BIND_LABEL,
1443
+ vector.w,
1444
+ )
1445
+ elif (
1446
+ material_color_bind_type
1447
+ == Vrm1MaterialColorBindPropertyGroup.TYPE_EMISSION_COLOR.identifier
1448
+ ):
1449
+ mtoon1.set_vector3(
1450
+ shader.OUTPUT_GROUP_NAME,
1451
+ shader.OUTPUT_GROUP_EXPRESSION_EMISSION_COLOR_BIND_LABEL,
1452
+ vector.xyz,
1453
+ )
1454
+ elif (
1455
+ material_color_bind_type
1456
+ == Vrm1MaterialColorBindPropertyGroup.TYPE_SHADE_COLOR.identifier
1457
+ ):
1458
+ mtoon1.set_vector3(
1459
+ shader.OUTPUT_GROUP_NAME,
1460
+ shader.OUTPUT_GROUP_EXPRESSION_SHADE_COLOR_BIND_LABEL,
1461
+ vector.xyz,
1462
+ )
1463
+ elif (
1464
+ material_color_bind_type
1465
+ == Vrm1MaterialColorBindPropertyGroup.TYPE_MATCAP_COLOR.identifier
1466
+ ):
1467
+ mtoon1.set_vector3(
1468
+ shader.OUTPUT_GROUP_NAME,
1469
+ shader.OUTPUT_GROUP_EXPRESSION_MATCAP_COLOR_BIND_LABEL,
1470
+ vector.xyz,
1471
+ )
1472
+ elif (
1473
+ material_color_bind_type
1474
+ == Vrm1MaterialColorBindPropertyGroup.TYPE_RIM_COLOR.identifier
1475
+ ):
1476
+ mtoon1.set_vector3(
1477
+ shader.OUTPUT_GROUP_NAME,
1478
+ shader.OUTPUT_GROUP_EXPRESSION_RIM_COLOR_BIND_LABEL,
1479
+ vector.xyz,
1480
+ )
1481
+ elif (
1482
+ material_color_bind_type
1483
+ == Vrm1MaterialColorBindPropertyGroup.TYPE_OUTLINE_COLOR.identifier
1484
+ ):
1485
+ mtoon1.set_vector3(
1486
+ shader.OUTPUT_GROUP_NAME,
1487
+ shader.OUTPUT_GROUP_EXPRESSION_OUTLINE_COLOR_BIND_LABEL,
1488
+ vector.xyz,
1489
+ )
1490
+
1491
+ mesh_data_name_to_key_block_name_to_total_value: dict[
1492
+ str, dict[str, float]
1493
+ ] = {}
1494
+ for (
1495
+ mesh_object_name,
1496
+ key_block_name_to_value,
1497
+ ) in mesh_object_name_to_key_block_name_to_value.items():
1498
+ mesh_object = context.blend_data.objects.get(mesh_object_name)
1499
+ if not mesh_object:
1500
+ continue
1501
+ mesh_data = mesh_object.data
1502
+ if not isinstance(mesh_data, Mesh):
1503
+ continue
1504
+ key_block_name_to_total_value = (
1505
+ mesh_data_name_to_key_block_name_to_total_value.get(mesh_data.name)
1506
+ )
1507
+ if not key_block_name_to_total_value:
1508
+ mesh_data_name_to_key_block_name_to_total_value[mesh_data.name] = (
1509
+ key_block_name_to_value
1510
+ )
1511
+ continue
1512
+ for key_block_name, value in key_block_name_to_value.items():
1513
+ key_block_name_to_total_value[key_block_name] = (
1514
+ key_block_name_to_total_value.get(key_block_name, 0) + value
1515
+ )
1516
+
1517
+ for (
1518
+ mesh_data_name,
1519
+ key_block_name_to_value,
1520
+ ) in mesh_data_name_to_key_block_name_to_total_value.items():
1521
+ mesh_data = context.blend_data.meshes.get(mesh_data_name)
1522
+ if not mesh_data:
1523
+ continue
1524
+ shape_keys = mesh_data.shape_keys
1525
+ if not shape_keys:
1526
+ continue
1527
+ key_blocks = shape_keys.key_blocks
1528
+ if not key_blocks:
1529
+ continue
1530
+ for key_block_name, value in key_block_name_to_value.items():
1531
+ key_block = key_blocks.get(key_block_name)
1532
+ if not key_block:
1533
+ continue
1534
+ if abs(key_block.value - value) < float_info.epsilon:
1535
+ continue
1536
+ key_block.value = value
1537
+
1538
+ is_binary: BoolProperty( # type: ignore[valid-type]
1539
+ name="Is Binary",
1540
+ update=_update_preview,
1541
+ )
1542
+ override_blink: EnumProperty( # type: ignore[valid-type]
1543
+ name="Override Blink",
1544
+ items=expression_override_type_enum.items(),
1545
+ update=_update_preview,
1546
+ )
1547
+ override_look_at: EnumProperty( # type: ignore[valid-type]
1548
+ name="Override Look At",
1549
+ items=expression_override_type_enum.items(),
1550
+ update=_update_preview,
1551
+ )
1552
+ override_mouth: EnumProperty( # type: ignore[valid-type]
1553
+ name="Override Mouth",
1554
+ items=expression_override_type_enum.items(),
1555
+ update=_update_preview,
1556
+ )
1557
+
1558
+ # for UI
1559
+ show_expanded: BoolProperty() # type: ignore[valid-type]
1560
+ show_expanded_morph_target_binds: BoolProperty( # type: ignore[valid-type]
1561
+ name="Morph Target Binds"
1562
+ )
1563
+ show_expanded_material_color_binds: BoolProperty( # type: ignore[valid-type]
1564
+ name="Material Color Binds"
1565
+ )
1566
+ show_expanded_texture_transform_binds: BoolProperty( # type: ignore[valid-type]
1567
+ name="Texture Transform Binds"
1568
+ )
1569
+
1570
+ def _get_preview(self) -> float:
1571
+ value = self.get("preview")
1572
+ if isinstance(value, (float, int)):
1573
+ return float(value)
1574
+ return 0.0
1575
+
1576
+ def _set_preview(self, value_obj: object) -> None:
1577
+ context = bpy.context
1578
+
1579
+ value = convert.float_or_none(value_obj)
1580
+ if value is None:
1581
+ return
1582
+
1583
+ current_value = convert.float_or_none(self.get("preview"))
1584
+ if (
1585
+ current_value is not None
1586
+ and abs(current_value - value) < float_info.epsilon
1587
+ ):
1588
+ return
1589
+
1590
+ self["preview"] = value
1591
+
1592
+ self._update_preview(context)
1593
+
1594
+ preview: FloatProperty( # type: ignore[valid-type]
1595
+ name="Expression",
1596
+ min=0,
1597
+ max=1,
1598
+ subtype="FACTOR",
1599
+ get=_get_preview,
1600
+ set=_set_preview,
1601
+ )
1602
+
1603
+ @classmethod
1604
+ def update_materials(cls, context: Context) -> None:
1605
+ for armature in context.blend_data.armatures:
1606
+ ext = get_armature_extension(armature).vrm1
1607
+ expressions = ext.expressions
1608
+ for expression in expressions.all_name_to_expression_dict().values():
1609
+ materials_to_update = expression.materials_to_update
1610
+ if not materials_to_update:
1611
+ continue
1612
+ for material_property_group in materials_to_update:
1613
+ material = material_property_group.material
1614
+ if not material:
1615
+ continue
1616
+ node_tree = material.node_tree
1617
+ if not node_tree:
1618
+ continue
1619
+
1620
+ # The update() method exists in the documentation, but
1621
+ # the call fails
1622
+ # https://docs.blender.org/api/4.2/bpy.types.NodeTree.html#bpy.types.NodeTree.update
1623
+ # node_tree.update()
1624
+ materials_to_update.clear()
1625
+
1626
+ active_morph_target_bind_index: IntProperty(min=0) # type: ignore[valid-type]
1627
+ active_material_color_bind_index: IntProperty(min=0) # type: ignore[valid-type]
1628
+ active_texture_transform_bind_index: IntProperty(min=0) # type: ignore[valid-type]
1629
+
1630
+ if TYPE_CHECKING:
1631
+ # This code is auto generated.
1632
+ # To regenerate, run the `uv run tools/property_typing.py` command.
1633
+ morph_target_binds: CollectionPropertyProtocol[ # type: ignore[no-redef]
1634
+ Vrm1MorphTargetBindPropertyGroup
1635
+ ]
1636
+ material_color_binds: CollectionPropertyProtocol[ # type: ignore[no-redef]
1637
+ Vrm1MaterialColorBindPropertyGroup
1638
+ ]
1639
+ texture_transform_binds: CollectionPropertyProtocol[ # type: ignore[no-redef]
1640
+ Vrm1TextureTransformBindPropertyGroup
1641
+ ]
1642
+ materials_to_update: CollectionPropertyProtocol[ # type: ignore[no-redef]
1643
+ MaterialPropertyGroup
1644
+ ]
1645
+ is_binary: bool # type: ignore[no-redef]
1646
+ override_blink: str # type: ignore[no-redef]
1647
+ override_look_at: str # type: ignore[no-redef]
1648
+ override_mouth: str # type: ignore[no-redef]
1649
+ show_expanded: bool # type: ignore[no-redef]
1650
+ show_expanded_morph_target_binds: bool # type: ignore[no-redef]
1651
+ show_expanded_material_color_binds: bool # type: ignore[no-redef]
1652
+ show_expanded_texture_transform_binds: bool # type: ignore[no-redef]
1653
+ preview: float # type: ignore[no-redef]
1654
+ active_morph_target_bind_index: int # type: ignore[no-redef]
1655
+ active_material_color_bind_index: int # type: ignore[no-redef]
1656
+ active_texture_transform_bind_index: int # type: ignore[no-redef]
1657
+
1658
+
1659
+ class Vrm1CustomExpressionPropertyGroup(Vrm1ExpressionPropertyGroup):
1660
+ def _get_custom_name(self) -> str:
1661
+ return str(self.get("custom_name", ""))
1662
+
1663
+ def _set_custom_name(self, value: str) -> None:
1664
+ if not value or self.get("custom_name") == value:
1665
+ return
1666
+
1667
+ armature_data = self.id_data
1668
+ if not isinstance(armature_data, Armature):
1669
+ _logger.error("No armature for %s", self)
1670
+ return
1671
+
1672
+ expressions = get_armature_extension(armature_data).vrm1.expressions
1673
+ all_expression_names = expressions.all_name_to_expression_dict().keys()
1674
+ custom_name = value
1675
+ for index in range(sys.maxsize):
1676
+ if index > 0:
1677
+ custom_name = f"{value}.{index:03}"
1678
+ if custom_name not in all_expression_names:
1679
+ break
1680
+
1681
+ self["custom_name"] = custom_name
1682
+ self.name = custom_name
1683
+
1684
+ custom_name: StringProperty( # type: ignore[valid-type]
1685
+ name="Name",
1686
+ get=_get_custom_name,
1687
+ set=_set_custom_name,
1688
+ )
1689
+
1690
+ if TYPE_CHECKING:
1691
+ # This code is auto generated.
1692
+ # To regenerate, run the `uv run tools/property_typing.py` command.
1693
+ custom_name: str # type: ignore[no-redef]
1694
+
1695
+
1696
+ # https://github.com/vrm-c/vrm-specification/blob/6fb6baaf9b9095a84fb82c8384db36e1afeb3558/specification/VRMC_vrm-1.0-beta/schema/VRMC_vrm.expressions.schema.json
1697
+ class Vrm1ExpressionsPresetPropertyGroup(PropertyGroup):
1698
+ happy: PointerProperty(type=Vrm1ExpressionPropertyGroup) # type: ignore[valid-type]
1699
+ angry: PointerProperty(type=Vrm1ExpressionPropertyGroup) # type: ignore[valid-type]
1700
+ sad: PointerProperty(type=Vrm1ExpressionPropertyGroup) # type: ignore[valid-type]
1701
+ relaxed: PointerProperty(type=Vrm1ExpressionPropertyGroup) # type: ignore[valid-type]
1702
+ surprised: PointerProperty(type=Vrm1ExpressionPropertyGroup) # type: ignore[valid-type]
1703
+ neutral: PointerProperty(type=Vrm1ExpressionPropertyGroup) # type: ignore[valid-type]
1704
+ aa: PointerProperty(type=Vrm1ExpressionPropertyGroup) # type: ignore[valid-type]
1705
+ ih: PointerProperty(type=Vrm1ExpressionPropertyGroup) # type: ignore[valid-type]
1706
+ ou: PointerProperty(type=Vrm1ExpressionPropertyGroup) # type: ignore[valid-type]
1707
+ ee: PointerProperty(type=Vrm1ExpressionPropertyGroup) # type: ignore[valid-type]
1708
+ oh: PointerProperty(type=Vrm1ExpressionPropertyGroup) # type: ignore[valid-type]
1709
+ blink: PointerProperty(type=Vrm1ExpressionPropertyGroup) # type: ignore[valid-type]
1710
+ blink_left: PointerProperty(type=Vrm1ExpressionPropertyGroup) # type: ignore[valid-type]
1711
+ blink_right: PointerProperty(type=Vrm1ExpressionPropertyGroup) # type: ignore[valid-type]
1712
+ look_up: PointerProperty(type=Vrm1ExpressionPropertyGroup) # type: ignore[valid-type]
1713
+ look_down: PointerProperty(type=Vrm1ExpressionPropertyGroup) # type: ignore[valid-type]
1714
+ look_left: PointerProperty(type=Vrm1ExpressionPropertyGroup) # type: ignore[valid-type]
1715
+ look_right: PointerProperty(type=Vrm1ExpressionPropertyGroup) # type: ignore[valid-type]
1716
+
1717
+ def expression_preset_and_expressions(
1718
+ self,
1719
+ ) -> tuple[tuple[ExpressionPreset, Vrm1ExpressionPropertyGroup], ...]:
1720
+ return (
1721
+ (
1722
+ ExpressionPresets.HAPPY,
1723
+ self.happy,
1724
+ ),
1725
+ (
1726
+ ExpressionPresets.ANGRY,
1727
+ self.angry,
1728
+ ),
1729
+ (
1730
+ ExpressionPresets.SAD,
1731
+ self.sad,
1732
+ ),
1733
+ (
1734
+ ExpressionPresets.RELAXED,
1735
+ self.relaxed,
1736
+ ),
1737
+ (
1738
+ ExpressionPresets.SURPRISED,
1739
+ self.surprised,
1740
+ ),
1741
+ (
1742
+ ExpressionPresets.NEUTRAL,
1743
+ self.neutral,
1744
+ ),
1745
+ (
1746
+ ExpressionPresets.AA,
1747
+ self.aa,
1748
+ ),
1749
+ (
1750
+ ExpressionPresets.IH,
1751
+ self.ih,
1752
+ ),
1753
+ (
1754
+ ExpressionPresets.OU,
1755
+ self.ou,
1756
+ ),
1757
+ (
1758
+ ExpressionPresets.EE,
1759
+ self.ee,
1760
+ ),
1761
+ (
1762
+ ExpressionPresets.OH,
1763
+ self.oh,
1764
+ ),
1765
+ (
1766
+ ExpressionPresets.BLINK,
1767
+ self.blink,
1768
+ ),
1769
+ (
1770
+ ExpressionPresets.BLINK_LEFT,
1771
+ self.blink_left,
1772
+ ),
1773
+ (
1774
+ ExpressionPresets.BLINK_RIGHT,
1775
+ self.blink_right,
1776
+ ),
1777
+ (
1778
+ ExpressionPresets.LOOK_UP,
1779
+ self.look_up,
1780
+ ),
1781
+ (
1782
+ ExpressionPresets.LOOK_DOWN,
1783
+ self.look_down,
1784
+ ),
1785
+ (
1786
+ ExpressionPresets.LOOK_LEFT,
1787
+ self.look_left,
1788
+ ),
1789
+ (
1790
+ ExpressionPresets.LOOK_RIGHT,
1791
+ self.look_right,
1792
+ ),
1793
+ )
1794
+
1795
+ def name_to_expression_dict(self) -> Mapping[str, Vrm1ExpressionPropertyGroup]:
1796
+ return {
1797
+ preset.name: property_group
1798
+ for preset, property_group in self.expression_preset_and_expressions()
1799
+ }
1800
+
1801
+ def is_mouth_expression(self, expression_name: str) -> bool:
1802
+ return next(
1803
+ (
1804
+ preset.mouth
1805
+ for preset, _ in self.expression_preset_and_expressions()
1806
+ if preset.name == expression_name
1807
+ ),
1808
+ False,
1809
+ )
1810
+
1811
+ def is_blink_expression(self, expression_name: str) -> bool:
1812
+ return next(
1813
+ (
1814
+ preset.blink
1815
+ for preset, _ in self.expression_preset_and_expressions()
1816
+ if preset.name == expression_name
1817
+ ),
1818
+ False,
1819
+ )
1820
+
1821
+ def get_icon(self, expression_name: str) -> Optional[str]:
1822
+ return next(
1823
+ (
1824
+ preset.icon
1825
+ for preset, _ in self.expression_preset_and_expressions()
1826
+ if preset.name == expression_name
1827
+ ),
1828
+ None,
1829
+ )
1830
+
1831
+ def is_look_at_expression(self, expression_name: str) -> bool:
1832
+ return next(
1833
+ (
1834
+ preset.look_at
1835
+ for preset, _ in self.expression_preset_and_expressions()
1836
+ if preset.name == expression_name
1837
+ ),
1838
+ False,
1839
+ )
1840
+
1841
+ if TYPE_CHECKING:
1842
+ # This code is auto generated.
1843
+ # To regenerate, run the `uv run tools/property_typing.py` command.
1844
+ happy: Vrm1ExpressionPropertyGroup # type: ignore[no-redef]
1845
+ angry: Vrm1ExpressionPropertyGroup # type: ignore[no-redef]
1846
+ sad: Vrm1ExpressionPropertyGroup # type: ignore[no-redef]
1847
+ relaxed: Vrm1ExpressionPropertyGroup # type: ignore[no-redef]
1848
+ surprised: Vrm1ExpressionPropertyGroup # type: ignore[no-redef]
1849
+ neutral: Vrm1ExpressionPropertyGroup # type: ignore[no-redef]
1850
+ aa: Vrm1ExpressionPropertyGroup # type: ignore[no-redef]
1851
+ ih: Vrm1ExpressionPropertyGroup # type: ignore[no-redef]
1852
+ ou: Vrm1ExpressionPropertyGroup # type: ignore[no-redef]
1853
+ ee: Vrm1ExpressionPropertyGroup # type: ignore[no-redef]
1854
+ oh: Vrm1ExpressionPropertyGroup # type: ignore[no-redef]
1855
+ blink: Vrm1ExpressionPropertyGroup # type: ignore[no-redef]
1856
+ blink_left: Vrm1ExpressionPropertyGroup # type: ignore[no-redef]
1857
+ blink_right: Vrm1ExpressionPropertyGroup # type: ignore[no-redef]
1858
+ look_up: Vrm1ExpressionPropertyGroup # type: ignore[no-redef]
1859
+ look_down: Vrm1ExpressionPropertyGroup # type: ignore[no-redef]
1860
+ look_left: Vrm1ExpressionPropertyGroup # type: ignore[no-redef]
1861
+ look_right: Vrm1ExpressionPropertyGroup # type: ignore[no-redef]
1862
+
1863
+
1864
+ # https://github.com/vrm-c/vrm-specification/blob/6fb6baaf9b9095a84fb82c8384db36e1afeb3558/specification/VRMC_vrm-1.0-beta/schema/VRMC_vrm.expressions.schema.json
1865
+ class Vrm1ExpressionsPropertyGroup(PropertyGroup):
1866
+ preset: PointerProperty( # type: ignore[valid-type]
1867
+ type=Vrm1ExpressionsPresetPropertyGroup,
1868
+ )
1869
+
1870
+ custom: CollectionProperty( # type: ignore[valid-type]
1871
+ type=Vrm1CustomExpressionPropertyGroup,
1872
+ )
1873
+
1874
+ def all_name_to_expression_dict(self) -> Mapping[str, Vrm1ExpressionPropertyGroup]:
1875
+ result: dict[str, Vrm1ExpressionPropertyGroup] = dict(
1876
+ self.preset.name_to_expression_dict()
1877
+ )
1878
+ for custom_expression in self.custom:
1879
+ result[custom_expression.custom_name] = custom_expression
1880
+ return result
1881
+
1882
+ # An empty element is held for the number of expressions for UIList
1883
+ # display of expressions
1884
+ expression_ui_list_elements: CollectionProperty( # type: ignore[valid-type]
1885
+ type=StringPropertyGroup
1886
+ )
1887
+ active_expression_ui_list_element_index: IntProperty(min=0) # type: ignore[valid-type]
1888
+
1889
+ initial_automatic_expression_assignment: BoolProperty( # type: ignore[valid-type]
1890
+ default=True
1891
+ )
1892
+
1893
+ def restore_expression_morph_target_bind_object_assignments(
1894
+ self, context: Context
1895
+ ) -> None:
1896
+ for expression in self.all_name_to_expression_dict().values():
1897
+ for morph_target_bind in expression.morph_target_binds:
1898
+ morph_target_bind.node.restore_object_assignment(context)
1899
+
1900
+ def fill_missing_expression_names(self) -> None:
1901
+ """Set a name for the expression preset.
1902
+
1903
+ It's not necessary for management, but I want to set it because it's
1904
+ displayed in the animation keyframes.
1905
+ """
1906
+ preset_name_to_expression_dict = self.preset.name_to_expression_dict()
1907
+ for preset_name, preset_expression in preset_name_to_expression_dict.items():
1908
+ if preset_expression.name != preset_name:
1909
+ preset_expression.name = preset_name
1910
+
1911
+ if TYPE_CHECKING:
1912
+ # This code is auto generated.
1913
+ # To regenerate, run the `uv run tools/property_typing.py` command.
1914
+ preset: Vrm1ExpressionsPresetPropertyGroup # type: ignore[no-redef]
1915
+ custom: CollectionPropertyProtocol[ # type: ignore[no-redef]
1916
+ Vrm1CustomExpressionPropertyGroup
1917
+ ]
1918
+ expression_ui_list_elements: CollectionPropertyProtocol[ # type: ignore[no-redef]
1919
+ StringPropertyGroup
1920
+ ]
1921
+ active_expression_ui_list_element_index: int # type: ignore[no-redef]
1922
+ initial_automatic_expression_assignment: bool # type: ignore[no-redef]
1923
+
1924
+
1925
+ # https://github.com/vrm-c/vrm-specification/blob/6fb6baaf9b9095a84fb82c8384db36e1afeb3558/specification/VRMC_vrm-1.0-beta/schema/VRMC_vrm.meta.schema.json
1926
+ class Vrm1MetaPropertyGroup(PropertyGroup):
1927
+ avatar_permission_enum, __avatar_permissions = property_group_enum(
1928
+ ("onlyAuthor", "Only Author", "", "NONE", 0),
1929
+ (
1930
+ "onlySeparatelyLicensedPerson",
1931
+ "Only Separately Licensed Person",
1932
+ "",
1933
+ "NONE",
1934
+ 1,
1935
+ ),
1936
+ ("everyone", "Everyone", "", "NONE", 2),
1937
+ )
1938
+
1939
+ commercial_usage_enum, __commercial_usage = property_group_enum(
1940
+ ("personalNonProfit", "Personal Non-Profit", "", "NONE", 0),
1941
+ ("personalProfit", "Personal Profit", "", "NONE", 1),
1942
+ ("corporation", "Corporation", "", "NONE", 2),
1943
+ )
1944
+
1945
+ credit_notation_enum, __credit_notation = property_group_enum(
1946
+ ("required", "Required", "", "NONE", 0),
1947
+ ("unnecessary", "Unnecessary", "", "NONE", 1),
1948
+ )
1949
+
1950
+ modification_enum, __modification = property_group_enum(
1951
+ ("prohibited", "Prohibited", "", "NONE", 0),
1952
+ ("allowModification", "Allow Modification", "", "NONE", 1),
1953
+ (
1954
+ "allowModificationRedistribution",
1955
+ "Allow Modification Redistribution",
1956
+ "",
1957
+ "NONE",
1958
+ 2,
1959
+ ),
1960
+ )
1961
+
1962
+ vrm_name: StringProperty( # type: ignore[valid-type]
1963
+ name="Name"
1964
+ )
1965
+ version: StringProperty( # type: ignore[valid-type]
1966
+ name="Version"
1967
+ )
1968
+ authors: CollectionProperty( # type: ignore[valid-type]
1969
+ type=StringPropertyGroup
1970
+ )
1971
+ copyright_information: StringProperty( # type: ignore[valid-type]
1972
+ name="Copyright Information"
1973
+ )
1974
+ contact_information: StringProperty( # type: ignore[valid-type]
1975
+ name="Contact Information"
1976
+ )
1977
+ references: CollectionProperty( # type: ignore[valid-type]
1978
+ type=StringPropertyGroup
1979
+ )
1980
+ third_party_licenses: StringProperty( # type: ignore[valid-type]
1981
+ name="Third Party Licenses"
1982
+ )
1983
+ thumbnail_image: PointerProperty( # type: ignore[valid-type]
1984
+ name="Thumbnail Image",
1985
+ type=Image,
1986
+ )
1987
+ # license_url: StringProperty( # type: ignore[valid-type]
1988
+ # name="License URL"
1989
+ # )
1990
+ avatar_permission: EnumProperty( # type: ignore[valid-type]
1991
+ name="Avatar Permission",
1992
+ items=avatar_permission_enum.items(),
1993
+ )
1994
+ allow_excessively_violent_usage: BoolProperty( # type: ignore[valid-type]
1995
+ name="Allow Excessively Violent Usage"
1996
+ )
1997
+ allow_excessively_sexual_usage: BoolProperty( # type: ignore[valid-type]
1998
+ name="Allow Excessively Sexual Usage"
1999
+ )
2000
+ commercial_usage: EnumProperty( # type: ignore[valid-type]
2001
+ name="Commercial Usage",
2002
+ items=commercial_usage_enum.items(),
2003
+ )
2004
+ allow_political_or_religious_usage: BoolProperty( # type: ignore[valid-type]
2005
+ name="Allow Political or Religious Usage"
2006
+ )
2007
+ allow_antisocial_or_hate_usage: BoolProperty( # type: ignore[valid-type]
2008
+ name="Allow Antisocial or Hate Usage"
2009
+ )
2010
+ credit_notation: EnumProperty( # type: ignore[valid-type]
2011
+ name="Credit Notation",
2012
+ items=credit_notation_enum.items(),
2013
+ )
2014
+ allow_redistribution: BoolProperty( # type: ignore[valid-type]
2015
+ name="Allow Redistribution"
2016
+ )
2017
+ modification: EnumProperty( # type: ignore[valid-type]
2018
+ name="Modification",
2019
+ items=modification_enum.items(),
2020
+ )
2021
+ other_license_url: StringProperty( # type: ignore[valid-type]
2022
+ name="Other License URL"
2023
+ )
2024
+
2025
+ # for UI
2026
+ active_author_index: IntProperty(min=0) # type: ignore[valid-type]
2027
+ active_reference_index: IntProperty(min=0) # type: ignore[valid-type]
2028
+
2029
+ if TYPE_CHECKING:
2030
+ # This code is auto generated.
2031
+ # To regenerate, run the `uv run tools/property_typing.py` command.
2032
+ vrm_name: str # type: ignore[no-redef]
2033
+ version: str # type: ignore[no-redef]
2034
+ authors: CollectionPropertyProtocol[ # type: ignore[no-redef]
2035
+ StringPropertyGroup
2036
+ ]
2037
+ copyright_information: str # type: ignore[no-redef]
2038
+ contact_information: str # type: ignore[no-redef]
2039
+ references: CollectionPropertyProtocol[ # type: ignore[no-redef]
2040
+ StringPropertyGroup
2041
+ ]
2042
+ third_party_licenses: str # type: ignore[no-redef]
2043
+ thumbnail_image: Optional[Image] # type: ignore[no-redef]
2044
+ avatar_permission: str # type: ignore[no-redef]
2045
+ allow_excessively_violent_usage: bool # type: ignore[no-redef]
2046
+ allow_excessively_sexual_usage: bool # type: ignore[no-redef]
2047
+ commercial_usage: str # type: ignore[no-redef]
2048
+ allow_political_or_religious_usage: bool # type: ignore[no-redef]
2049
+ allow_antisocial_or_hate_usage: bool # type: ignore[no-redef]
2050
+ credit_notation: str # type: ignore[no-redef]
2051
+ allow_redistribution: bool # type: ignore[no-redef]
2052
+ modification: str # type: ignore[no-redef]
2053
+ other_license_url: str # type: ignore[no-redef]
2054
+ active_author_index: int # type: ignore[no-redef]
2055
+ active_reference_index: int # type: ignore[no-redef]
2056
+
2057
+
2058
+ # https://github.com/vrm-c/vrm-specification/blob/6fb6baaf9b9095a84fb82c8384db36e1afeb3558/specification/VRMC_vrm-1.0-beta/schema/VRMC_vrm.schema.json
2059
+ class Vrm1PropertyGroup(PropertyGroup):
2060
+ meta: PointerProperty( # type: ignore[valid-type]
2061
+ type=Vrm1MetaPropertyGroup
2062
+ )
2063
+ humanoid: PointerProperty( # type: ignore[valid-type]
2064
+ type=Vrm1HumanoidPropertyGroup
2065
+ )
2066
+ first_person: PointerProperty( # type: ignore[valid-type]
2067
+ type=Vrm1FirstPersonPropertyGroup
2068
+ )
2069
+ look_at: PointerProperty( # type: ignore[valid-type]
2070
+ type=Vrm1LookAtPropertyGroup
2071
+ )
2072
+ expressions: PointerProperty( # type: ignore[valid-type]
2073
+ type=Vrm1ExpressionsPropertyGroup
2074
+ )
2075
+ if TYPE_CHECKING:
2076
+ # This code is auto generated.
2077
+ # To regenerate, run the `uv run tools/property_typing.py` command.
2078
+ meta: Vrm1MetaPropertyGroup # type: ignore[no-redef]
2079
+ humanoid: Vrm1HumanoidPropertyGroup # type: ignore[no-redef]
2080
+ first_person: Vrm1FirstPersonPropertyGroup # type: ignore[no-redef]
2081
+ look_at: Vrm1LookAtPropertyGroup # type: ignore[no-redef]
2082
+ expressions: Vrm1ExpressionsPropertyGroup # type: ignore[no-redef]
2083
+
2084
+
2085
+ def clear_global_variables() -> None:
2086
+ Vrm1HumanBonesPropertyGroup.pointer_to_last_bone_names_str.clear()
2087
+ Vrm1ExpressionPropertyGroup.pending_preview_update_armature_data_names.clear()