@volter/blender-engine 0.1.148 → 0.1.150

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 +22 -5
  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 +66 -129
  185. package/browser/blender-engine.mts +20 -186
  186. package/browser/blender-wali-engine.mts +37 -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 +3 -0
  192. package/browser/protocol.ts +130 -43
  193. package/browser/pull-job.mts +2 -10
  194. package/browser/rna.ts +190 -73
  195. package/browser/runtime.ts +271 -106
  196. package/browser/session-addons.mts +33 -0
  197. package/browser/session-frame.mts +5 -24
  198. package/browser/session.py +1389 -1446
  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 +492 -306
  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 +195 -180
  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,930 @@
1
+ # SPDX-License-Identifier: MIT OR GPL-3.0-or-later
2
+ import traceback
3
+ from pathlib import Path
4
+ from typing import TYPE_CHECKING, ClassVar, Optional
5
+
6
+ import bpy
7
+ from bpy.app.translations import pgettext
8
+ from bpy.props import BoolProperty, CollectionProperty, StringProperty
9
+ from bpy.types import (
10
+ Armature,
11
+ Context,
12
+ Event,
13
+ Object,
14
+ Operator,
15
+ Panel,
16
+ SpaceFileBrowser,
17
+ UILayout,
18
+ )
19
+ from bpy_extras.io_utils import ExportHelper
20
+
21
+ from ..common import ops, safe_removal, version
22
+ from ..common.error_dialog import show_error_dialog
23
+ from ..common.logger import get_logger
24
+ from ..common.preferences import (
25
+ ExportPreferencesProtocol,
26
+ copy_export_preferences,
27
+ draw_export_preferences_layout,
28
+ get_preferences,
29
+ )
30
+ from ..common.workspace import save_workspace
31
+ from ..editor import migration, search, validation
32
+ from ..editor.extension_accessor import get_armature_extension
33
+ from ..editor.ops import VRM_OT_open_url_in_web_browser, layout_operator
34
+ from ..editor.property_group import CollectionPropertyProtocol, StringPropertyGroup
35
+ from ..editor.validation import VrmValidationError, WM_OT_vrm_validator
36
+ from ..editor.vrm0.ops import assign_vrm0_humanoid_human_bones_automatically
37
+ from ..editor.vrm0.panel import (
38
+ draw_vrm0_humanoid_operators_layout,
39
+ draw_vrm0_humanoid_optional_bones_layout,
40
+ draw_vrm0_humanoid_required_bones_layout,
41
+ )
42
+ from ..editor.vrm0.property_group import Vrm0HumanoidPropertyGroup
43
+ from ..editor.vrm1.ops import (
44
+ VRM_OT_assign_vrm1_humanoid_human_bones_automatically,
45
+ assign_vrm1_humanoid_human_bones_automatically,
46
+ )
47
+ from ..editor.vrm1.panel import (
48
+ draw_vrm1_humanoid_optional_bones_layout,
49
+ draw_vrm1_humanoid_required_bones_layout,
50
+ )
51
+ from ..editor.vrm1.property_group import Vrm1HumanBonesPropertyGroup
52
+ from .abstract_base_vrm_exporter import AbstractBaseVrmExporter
53
+ from .khr_character_exporter import KhrCharacterExporter
54
+ from .vrm0_exporter import Vrm0Exporter
55
+ from .vrm1_exporter import Vrm1Exporter
56
+ from .vrm_animation_exporter import VrmAnimationExporter
57
+
58
+ _logger = get_logger(__name__)
59
+
60
+
61
+ def _export_vrm_update_addon_preferences(
62
+ export_op: "EXPORT_SCENE_OT_vrm", context: Context
63
+ ) -> None:
64
+ if export_op.use_addon_preferences:
65
+ copy_export_preferences(source=export_op, destination=get_preferences(context))
66
+
67
+ WM_OT_vrm_validator.detect_errors(
68
+ context,
69
+ export_op.errors,
70
+ export_op.armature_object_name,
71
+ )
72
+
73
+
74
+ class EXPORT_SCENE_OT_vrm(Operator, ExportHelper):
75
+ bl_idname = "export_scene.vrm"
76
+ bl_label = "Save"
77
+ bl_description = "export VRM"
78
+ bl_options: ClassVar = {"REGISTER"}
79
+
80
+ filename_ext = ".vrm"
81
+ filter_glob: StringProperty( # type: ignore[valid-type]
82
+ default="*.vrm",
83
+ options={"HIDDEN"},
84
+ )
85
+
86
+ use_addon_preferences: BoolProperty( # type: ignore[valid-type]
87
+ name="Export using add-on preferences",
88
+ description="Export using add-on preferences instead of operator arguments",
89
+ )
90
+ export_invisibles: BoolProperty( # type: ignore[valid-type]
91
+ name="Export Invisible Objects",
92
+ update=_export_vrm_update_addon_preferences,
93
+ )
94
+ export_only_selections: BoolProperty( # type: ignore[valid-type]
95
+ name="Export Only Selections",
96
+ update=_export_vrm_update_addon_preferences,
97
+ )
98
+ enable_advanced_preferences: BoolProperty( # type: ignore[valid-type]
99
+ name="Enable Advanced Options",
100
+ update=_export_vrm_update_addon_preferences,
101
+ )
102
+ export_all_influences: BoolProperty( # type: ignore[valid-type]
103
+ name="Export All Bone Influences",
104
+ update=_export_vrm_update_addon_preferences,
105
+ )
106
+ export_lights: BoolProperty( # type: ignore[valid-type]
107
+ name="Export Lights",
108
+ update=_export_vrm_update_addon_preferences,
109
+ )
110
+ export_gltf_animations: BoolProperty( # type: ignore[valid-type]
111
+ name="Export glTF Animations",
112
+ update=_export_vrm_update_addon_preferences,
113
+ )
114
+ export_try_sparse_sk: BoolProperty( # type: ignore[valid-type]
115
+ name="Use Sparse Accessors",
116
+ update=_export_vrm_update_addon_preferences,
117
+ )
118
+
119
+ errors: CollectionProperty( # type: ignore[valid-type]
120
+ type=validation.VrmValidationError,
121
+ options={"HIDDEN"},
122
+ )
123
+ armature_object_name: StringProperty( # type: ignore[valid-type]
124
+ options={"HIDDEN"},
125
+ )
126
+ ignore_warning: BoolProperty( # type: ignore[valid-type]
127
+ options={"HIDDEN"},
128
+ )
129
+
130
+ def execute(self, context: Context) -> set[str]:
131
+ try:
132
+ if not self.filepath:
133
+ return {"CANCELLED"}
134
+
135
+ if self.use_addon_preferences:
136
+ copy_export_preferences(
137
+ source=get_preferences(context), destination=self
138
+ )
139
+
140
+ return _export_vrm(
141
+ Path(self.filepath),
142
+ self,
143
+ context,
144
+ armature_object_name=self.armature_object_name,
145
+ )
146
+ except Exception:
147
+ show_error_dialog(
148
+ pgettext("Failed to export VRM."),
149
+ traceback.format_exc(),
150
+ )
151
+ raise
152
+
153
+ def invoke(self, context: Context, event: Event) -> set[str]:
154
+ self.use_addon_preferences = True
155
+ copy_export_preferences(source=get_preferences(context), destination=self)
156
+
157
+ if "gltf" not in dir(bpy.ops.export_scene):
158
+ return ops.wm.vrm_gltf2_addon_disabled_warning(
159
+ "INVOKE_DEFAULT",
160
+ )
161
+
162
+ armatures, _ = _collect_export_objects(
163
+ context,
164
+ self.armature_object_name,
165
+ self,
166
+ )
167
+ if len(armatures) > 1:
168
+ return ops.wm.vrm_export_armature_selection("INVOKE_DEFAULT")
169
+ if len(armatures) == 1:
170
+ armature = armatures[0]
171
+ armature_data = armature.data
172
+ if not isinstance(armature_data, Armature):
173
+ return {"CANCELLED"}
174
+ ext = get_armature_extension(armature_data)
175
+ if ext.is_vrm0():
176
+ Vrm0HumanoidPropertyGroup.fixup_human_bones(armature)
177
+ humanoid = ext.vrm0.humanoid
178
+ if not humanoid.bones_are_correctly_assigned():
179
+ if humanoid.initial_automatic_bone_assignment:
180
+ humanoid.initial_automatic_bone_assignment = False
181
+ assign_vrm0_humanoid_human_bones_automatically(
182
+ context, armature
183
+ )
184
+ if not humanoid.bones_are_correctly_assigned():
185
+ return ops.wm.vrm_export_human_bones_assignment(
186
+ "INVOKE_DEFAULT",
187
+ armature_object_name=self.armature_object_name,
188
+ )
189
+ elif ext.is_vrm1():
190
+ Vrm1HumanBonesPropertyGroup.fixup_human_bones(armature)
191
+ human_bones = ext.vrm1.humanoid.human_bones
192
+ if not human_bones.bones_are_correctly_assigned():
193
+ if human_bones.initial_automatic_bone_assignment:
194
+ human_bones.initial_automatic_bone_assignment = False
195
+ assign_vrm1_humanoid_human_bones_automatically(
196
+ context, armature
197
+ )
198
+ if not human_bones.bones_are_correctly_assigned():
199
+ return ops.wm.vrm_export_human_bones_assignment(
200
+ "INVOKE_DEFAULT",
201
+ armature_object_name=self.armature_object_name,
202
+ )
203
+
204
+ if WM_OT_vrm_validator.detect_errors(
205
+ context,
206
+ self.errors,
207
+ self.armature_object_name,
208
+ execute_migration=True,
209
+ ):
210
+ return ops.vrm.model_validate(
211
+ "INVOKE_DEFAULT",
212
+ show_successful_message=False,
213
+ armature_object_name=self.armature_object_name,
214
+ )
215
+ if not self.ignore_warning and any(
216
+ error.severity <= 1 for error in self.errors
217
+ ):
218
+ return ops.wm.vrm_export_confirmation(
219
+ "INVOKE_DEFAULT", armature_object_name=self.armature_object_name
220
+ )
221
+
222
+ return ExportHelper.invoke(self, context, event)
223
+
224
+ def draw(self, _context: Context) -> None:
225
+ pass # Is needed to get panels available
226
+
227
+ if TYPE_CHECKING:
228
+ # This code is auto generated.
229
+ # To regenerate, run the `uv run tools/property_typing.py` command.
230
+ filter_glob: str # type: ignore[no-redef]
231
+ use_addon_preferences: bool # type: ignore[no-redef]
232
+ export_invisibles: bool # type: ignore[no-redef]
233
+ export_only_selections: bool # type: ignore[no-redef]
234
+ enable_advanced_preferences: bool # type: ignore[no-redef]
235
+ export_all_influences: bool # type: ignore[no-redef]
236
+ export_lights: bool # type: ignore[no-redef]
237
+ export_gltf_animations: bool # type: ignore[no-redef]
238
+ export_try_sparse_sk: bool # type: ignore[no-redef]
239
+ errors: CollectionPropertyProtocol[ # type: ignore[no-redef]
240
+ VrmValidationError
241
+ ]
242
+ armature_object_name: str # type: ignore[no-redef]
243
+ ignore_warning: bool # type: ignore[no-redef]
244
+
245
+
246
+ def _export_vrm(
247
+ filepath: Path,
248
+ export_preferences: ExportPreferencesProtocol,
249
+ context: Context,
250
+ *,
251
+ armature_object_name: str,
252
+ ) -> set[str]:
253
+ if WM_OT_vrm_validator.detect_errors(
254
+ context,
255
+ error_collection=None,
256
+ armature_object_name=armature_object_name,
257
+ execute_migration=True,
258
+ ):
259
+ return {"CANCELLED"}
260
+
261
+ armature_objects, export_objects = _collect_export_objects(
262
+ context,
263
+ armature_object_name,
264
+ export_preferences,
265
+ )
266
+
267
+ armature_object: Optional[Object] = next(iter(armature_objects), None)
268
+ is_vrm1 = False
269
+ is_khr_character = False
270
+ armature_object_is_temporary = False
271
+ try:
272
+ with save_workspace(
273
+ context,
274
+ # Allow restoring after changing active object
275
+ armature_object,
276
+ ):
277
+ if armature_object:
278
+ armature_data = armature_object.data
279
+ if isinstance(armature_data, Armature):
280
+ is_vrm1 = get_armature_extension(armature_data).is_vrm1()
281
+ is_khr_character = get_armature_extension(
282
+ armature_data
283
+ ).is_khr_character()
284
+ else:
285
+ ops.icyp.make_basic_armature("EXEC_DEFAULT")
286
+ armature_object = context.view_layer.objects.active
287
+ if not armature_object or armature_object.type != "ARMATURE":
288
+ message = "Failed to generate temporary armature"
289
+ raise RuntimeError(message)
290
+ armature_object_is_temporary = True
291
+
292
+ migration.migrate(context, armature_object.name, heavy_migration=True)
293
+
294
+ if is_khr_character:
295
+ exporter: AbstractBaseVrmExporter = KhrCharacterExporter(
296
+ context,
297
+ export_objects,
298
+ armature_object,
299
+ export_preferences,
300
+ )
301
+ elif is_vrm1:
302
+ exporter: AbstractBaseVrmExporter = Vrm1Exporter(
303
+ context,
304
+ export_objects,
305
+ armature_object,
306
+ export_preferences,
307
+ )
308
+ else:
309
+ exporter: AbstractBaseVrmExporter = Vrm0Exporter(
310
+ context,
311
+ export_objects,
312
+ armature_object,
313
+ )
314
+
315
+ try:
316
+ glb_bytes = exporter.export()
317
+ except Exception as e:
318
+ message = (
319
+ "Failed to export VRM with "
320
+ + type(exporter).__name__
321
+ + exporter.generator_postfix
322
+ )
323
+ raise RuntimeError(message) from e
324
+ finally:
325
+ if armature_object and armature_object_is_temporary:
326
+ if not isinstance(armature_data := armature_object.data, Armature):
327
+ armature_data = None
328
+ if not safe_removal.remove_object(context, armature_object):
329
+ _logger.warning("Failed to remove temporary armature")
330
+ if armature_data:
331
+ if armature_data.users:
332
+ _logger.warning(
333
+ 'Failed to remove "%s" with %d users '
334
+ "while removing temporary armature",
335
+ armature_data.name,
336
+ armature_data.users,
337
+ )
338
+ else:
339
+ context.blend_data.armatures.remove(armature_data)
340
+
341
+ if glb_bytes is None:
342
+ return {"CANCELLED"}
343
+
344
+ Path(filepath).write_bytes(glb_bytes)
345
+
346
+ return {"FINISHED"}
347
+
348
+
349
+ class VRM_PT_export_file_browser_tool_props(Panel):
350
+ bl_idname = "VRM_PT_export_file_browser_tool_props"
351
+ bl_space_type = "FILE_BROWSER"
352
+ bl_region_type = "TOOL_PROPS"
353
+ bl_parent_id = "FILE_PT_operator"
354
+ bl_label = ""
355
+ bl_options: ClassVar = {"HIDE_HEADER"}
356
+
357
+ @classmethod
358
+ def poll(cls, context: Context) -> bool:
359
+ space_data = context.space_data
360
+ if not isinstance(space_data, SpaceFileBrowser):
361
+ return False
362
+ return space_data.active_operator.bl_idname == "EXPORT_SCENE_OT_vrm"
363
+
364
+ def draw(self, context: Context) -> None:
365
+ space_data = context.space_data
366
+ if not isinstance(space_data, SpaceFileBrowser):
367
+ return
368
+
369
+ operator = space_data.active_operator
370
+ if not isinstance(operator, EXPORT_SCENE_OT_vrm):
371
+ return
372
+
373
+ layout = self.layout
374
+
375
+ warning_message = version.panel_warning_message()
376
+ if warning_message:
377
+ box = layout.box()
378
+ warning_column = box.column(align=True)
379
+ for index, warning_line in enumerate(warning_message.splitlines()):
380
+ warning_column.label(
381
+ text=warning_line,
382
+ translate=False,
383
+ icon="NONE" if index else "ERROR",
384
+ )
385
+
386
+ show_vrm1_options = False
387
+ armature_objects, _ = _collect_export_objects(
388
+ context, operator.armature_object_name, operator
389
+ )
390
+ if (
391
+ armature_objects
392
+ and (armature_object := armature_objects[0])
393
+ and (armature_data := armature_object.data)
394
+ and isinstance(armature_data, Armature)
395
+ ):
396
+ show_vrm1_options = get_armature_extension(armature_data).is_vrm1()
397
+
398
+ draw_export_preferences_layout(
399
+ operator,
400
+ layout,
401
+ show_vrm1_options=show_vrm1_options,
402
+ )
403
+
404
+ if operator.errors:
405
+ WM_OT_vrm_validator.draw_errors(
406
+ layout.box(),
407
+ operator.errors,
408
+ show_successful_message=False,
409
+ )
410
+
411
+
412
+ class VRM_PT_export_vrma_help(Panel):
413
+ bl_idname = "VRM_PT_export_vrma_help"
414
+ bl_space_type = "FILE_BROWSER"
415
+ bl_region_type = "TOOL_PROPS"
416
+ bl_parent_id = "FILE_PT_operator"
417
+ bl_label = ""
418
+ bl_options: ClassVar = {"HIDE_HEADER"}
419
+
420
+ @classmethod
421
+ def poll(cls, context: Context) -> bool:
422
+ space_data = context.space_data
423
+ if not isinstance(space_data, SpaceFileBrowser):
424
+ return False
425
+ return space_data.active_operator.bl_idname == "EXPORT_SCENE_OT_vrma"
426
+
427
+ def draw(self, _context: Context) -> None:
428
+ _draw_help_message(self.layout)
429
+
430
+
431
+ def menu_export(menu_op: Operator, _context: Context) -> None:
432
+ vrm_export_op = layout_operator(
433
+ menu_op.layout, EXPORT_SCENE_OT_vrm, text="VRM (.vrm)"
434
+ )
435
+ vrm_export_op.use_addon_preferences = True
436
+ vrm_export_op.armature_object_name = ""
437
+ vrm_export_op.ignore_warning = False
438
+
439
+ vrma_export_op = layout_operator(
440
+ menu_op.layout, EXPORT_SCENE_OT_vrma, text="VRM Animation (.vrma)"
441
+ )
442
+ vrma_export_op.armature_object_name = ""
443
+
444
+
445
+ class EXPORT_SCENE_OT_vrma(Operator, ExportHelper):
446
+ bl_idname = "export_scene.vrma"
447
+ bl_label = "Save"
448
+ bl_description = "Export VRM Animation"
449
+ bl_options: ClassVar = {"REGISTER"}
450
+
451
+ filename_ext = ".vrma"
452
+ filter_glob: StringProperty( # type: ignore[valid-type]
453
+ default="*.vrma",
454
+ options={"HIDDEN"},
455
+ )
456
+
457
+ armature_object_name: StringProperty( # type: ignore[valid-type]
458
+ options={"HIDDEN"},
459
+ )
460
+
461
+ def execute(self, context: Context) -> set[str]:
462
+ try:
463
+ if WM_OT_vrma_export_prerequisite.detect_errors(
464
+ context, self.armature_object_name
465
+ ):
466
+ return {"CANCELLED"}
467
+ if not self.filepath:
468
+ return {"CANCELLED"}
469
+ if not self.armature_object_name:
470
+ armature = search.current_armature(context)
471
+ else:
472
+ armature = context.blend_data.objects.get(self.armature_object_name)
473
+ if not armature:
474
+ return {"CANCELLED"}
475
+ return VrmAnimationExporter.execute(context, Path(self.filepath), armature)
476
+ except Exception:
477
+ show_error_dialog(
478
+ pgettext("Failed to export VRM Animation."),
479
+ traceback.format_exc(),
480
+ )
481
+ raise
482
+
483
+ def invoke(self, context: Context, event: Event) -> set[str]:
484
+ if WM_OT_vrma_export_prerequisite.detect_errors(
485
+ context, self.armature_object_name
486
+ ):
487
+ return ops.wm.vrma_export_prerequisite(
488
+ "INVOKE_DEFAULT",
489
+ armature_object_name=self.armature_object_name,
490
+ )
491
+ return ExportHelper.invoke(self, context, event)
492
+
493
+ def draw(self, _context: Context) -> None:
494
+ pass # Is needed to get panels available
495
+
496
+ if TYPE_CHECKING:
497
+ # This code is auto generated.
498
+ # To regenerate, run the `uv run tools/property_typing.py` command.
499
+ filter_glob: str # type: ignore[no-redef]
500
+ armature_object_name: str # type: ignore[no-redef]
501
+
502
+
503
+ class WM_OT_vrm_export_human_bones_assignment(Operator):
504
+ bl_label = "Required VRM Human Bones Assignment"
505
+ bl_idname = "wm.vrm_export_human_bones_assignment"
506
+ bl_options: ClassVar = {"REGISTER"}
507
+
508
+ armature_object_name: StringProperty( # type: ignore[valid-type]
509
+ options={"HIDDEN"},
510
+ )
511
+
512
+ def execute(self, context: Context) -> set[str]:
513
+ preferences = get_preferences(context)
514
+ armatures, _ = _collect_export_objects(
515
+ context,
516
+ self.armature_object_name,
517
+ preferences,
518
+ )
519
+ if len(armatures) != 1:
520
+ return {"CANCELLED"}
521
+ armature = armatures[0]
522
+ armature_data = armature.data
523
+ if not isinstance(armature_data, Armature):
524
+ return {"CANCELLED"}
525
+ if get_armature_extension(armature_data).is_vrm0():
526
+ Vrm0HumanoidPropertyGroup.fixup_human_bones(armature)
527
+ Vrm0HumanoidPropertyGroup.update_all_bone_name_candidates(
528
+ context, armature_data.name
529
+ )
530
+ humanoid = get_armature_extension(armature_data).vrm0.humanoid
531
+ if not humanoid.bones_are_correctly_assigned():
532
+ return {"CANCELLED"}
533
+ elif get_armature_extension(armature_data).is_vrm1():
534
+ Vrm1HumanBonesPropertyGroup.fixup_human_bones(armature)
535
+ Vrm1HumanBonesPropertyGroup.update_all_bone_name_candidates(
536
+ context, armature_data.name
537
+ )
538
+ human_bones = get_armature_extension(
539
+ armature_data
540
+ ).vrm1.humanoid.human_bones
541
+ if not human_bones.bones_are_correctly_assigned():
542
+ return {"CANCELLED"}
543
+ else:
544
+ return {"CANCELLED"}
545
+ return ops.export_scene.vrm(
546
+ "INVOKE_DEFAULT", armature_object_name=self.armature_object_name
547
+ )
548
+
549
+ def invoke(self, context: Context, _event: Event) -> set[str]:
550
+ return context.window_manager.invoke_props_dialog(self, width=800)
551
+
552
+ def draw(self, context: Context) -> None:
553
+ preferences = get_preferences(context)
554
+ armatures, _ = _collect_export_objects(
555
+ context, self.armature_object_name, preferences
556
+ )
557
+ if not armatures:
558
+ return
559
+ armature = armatures[0]
560
+ armature_data = armature.data
561
+ if not isinstance(armature_data, Armature):
562
+ return
563
+
564
+ if get_armature_extension(armature_data).is_vrm0():
565
+ WM_OT_vrm_export_human_bones_assignment.draw_vrm0(self.layout, armature)
566
+ elif get_armature_extension(armature_data).is_vrm1():
567
+ WM_OT_vrm_export_human_bones_assignment.draw_vrm1(self.layout, armature)
568
+
569
+ @staticmethod
570
+ def draw_vrm0(layout: UILayout, armature: Object) -> None:
571
+ armature_data = armature.data
572
+ if not isinstance(armature_data, Armature):
573
+ return
574
+
575
+ humanoid = get_armature_extension(armature_data).vrm0.humanoid
576
+ error_messages = humanoid.error_messages()
577
+ if not error_messages:
578
+ alert_box = layout.box()
579
+ alert_box.label(
580
+ text="All Required VRM Human Bones have been assigned.",
581
+ icon="CHECKMARK",
582
+ )
583
+ else:
584
+ alert_box = layout.box()
585
+ alert_box.alert = True
586
+ alert_column = alert_box.column(align=True)
587
+ for error_message in error_messages:
588
+ alert_column.label(text=error_message, translate=False, icon="ERROR")
589
+ draw_vrm0_humanoid_operators_layout(armature, layout)
590
+ layout.prop(humanoid, "filter_by_human_bone_hierarchy")
591
+ row = layout.split(factor=0.5)
592
+ draw_vrm0_humanoid_required_bones_layout(armature, row.column())
593
+ draw_vrm0_humanoid_optional_bones_layout(armature, row.column())
594
+
595
+ @staticmethod
596
+ def draw_vrm1(layout: UILayout, armature: Object) -> None:
597
+ armature_data = armature.data
598
+ if not isinstance(armature_data, Armature):
599
+ return
600
+
601
+ human_bones = get_armature_extension(armature_data).vrm1.humanoid.human_bones
602
+ if human_bones.allow_non_humanoid_rig:
603
+ layout.prop(human_bones, "allow_non_humanoid_rig")
604
+ non_humanoid_warnings_box = layout.box()
605
+ non_humanoid_warnings_column = non_humanoid_warnings_box.column(align=True)
606
+ text = pgettext(
607
+ "VRMs exported as Non-Humanoid\n"
608
+ + "Rigs can not have animations applied\n"
609
+ + "for humanoid avatars."
610
+ )
611
+ for index, message in enumerate(text.splitlines()):
612
+ non_humanoid_warnings_column.label(
613
+ text=message,
614
+ translate=False,
615
+ icon="ERROR" if index == 0 else "NONE",
616
+ )
617
+ return
618
+
619
+ if human_bones.bones_are_correctly_assigned():
620
+ alert_box = layout.box()
621
+ alert_box.label(
622
+ text="All Required VRM Human Bones have been assigned.",
623
+ icon="CHECKMARK",
624
+ )
625
+ else:
626
+ alert_box = layout.box()
627
+ alert_box.alert = True
628
+ alert_column = alert_box.column(align=True)
629
+ for error_message in human_bones.error_messages():
630
+ alert_column.label(text=error_message, translate=False, icon="ERROR")
631
+
632
+ layout_operator(
633
+ layout,
634
+ VRM_OT_assign_vrm1_humanoid_human_bones_automatically,
635
+ icon="ARMATURE_DATA",
636
+ ).armature_object_name = armature.name
637
+
638
+ layout.prop(human_bones, "filter_by_human_bone_hierarchy")
639
+ row = layout.split(factor=0.5)
640
+ draw_vrm1_humanoid_required_bones_layout(armature, row.column())
641
+ draw_vrm1_humanoid_optional_bones_layout(armature, row.column())
642
+
643
+ layout.prop(human_bones, "allow_non_humanoid_rig")
644
+
645
+ if TYPE_CHECKING:
646
+ # This code is auto generated.
647
+ # To regenerate, run the `uv run tools/property_typing.py` command.
648
+ armature_object_name: str # type: ignore[no-redef]
649
+
650
+
651
+ class WM_OT_vrm_export_confirmation(Operator):
652
+ bl_label = "VRM Export Confirmation"
653
+ bl_idname = "wm.vrm_export_confirmation"
654
+ bl_options: ClassVar = {"REGISTER"}
655
+
656
+ errors: CollectionProperty(type=validation.VrmValidationError) # type: ignore[valid-type]
657
+
658
+ armature_object_name: StringProperty( # type: ignore[valid-type]
659
+ options={"HIDDEN"},
660
+ )
661
+
662
+ export_anyway: BoolProperty( # type: ignore[valid-type]
663
+ name="Export Anyway",
664
+ )
665
+
666
+ def execute(self, _context: Context) -> set[str]:
667
+ if not self.export_anyway:
668
+ return {"CANCELLED"}
669
+ ops.export_scene.vrm(
670
+ "INVOKE_DEFAULT",
671
+ ignore_warning=True,
672
+ armature_object_name=self.armature_object_name,
673
+ )
674
+ return {"FINISHED"}
675
+
676
+ def invoke(self, context: Context, _event: Event) -> set[str]:
677
+ WM_OT_vrm_validator.detect_errors(
678
+ context,
679
+ self.errors,
680
+ self.armature_object_name,
681
+ )
682
+ return context.window_manager.invoke_props_dialog(self, width=800)
683
+
684
+ def draw(self, _context: Context) -> None:
685
+ layout = self.layout
686
+ layout.label(
687
+ text="There is a high-impact warning. VRM may not export as intended.",
688
+ icon="ERROR",
689
+ )
690
+
691
+ column = layout.column()
692
+ for error in self.errors:
693
+ if error.severity != 1:
694
+ continue
695
+ column.prop(
696
+ error,
697
+ "message",
698
+ text="",
699
+ translate=False,
700
+ )
701
+
702
+ layout.prop(self, "export_anyway")
703
+
704
+ if TYPE_CHECKING:
705
+ # This code is auto generated.
706
+ # To regenerate, run the `uv run tools/property_typing.py` command.
707
+ errors: CollectionPropertyProtocol[ # type: ignore[no-redef]
708
+ VrmValidationError
709
+ ]
710
+ armature_object_name: str # type: ignore[no-redef]
711
+ export_anyway: bool # type: ignore[no-redef]
712
+
713
+
714
+ class WM_OT_vrm_export_armature_selection(Operator):
715
+ bl_label = "VRM Export Armature Selection"
716
+ bl_idname = "wm.vrm_export_armature_selection"
717
+ bl_options: ClassVar = {"REGISTER"}
718
+
719
+ armature_object_name: StringProperty( # type: ignore[valid-type]
720
+ options={"HIDDEN"},
721
+ )
722
+ armature_object_name_candidates: CollectionProperty( # type: ignore[valid-type]
723
+ type=StringPropertyGroup,
724
+ options={"HIDDEN"},
725
+ )
726
+
727
+ def execute(self, context: Context) -> set[str]:
728
+ if not self.armature_object_name:
729
+ return {"CANCELLED"}
730
+ armature_object = context.blend_data.objects.get(self.armature_object_name)
731
+ if not armature_object or armature_object.type != "ARMATURE":
732
+ return {"CANCELLED"}
733
+ ops.export_scene.vrm(
734
+ "INVOKE_DEFAULT", armature_object_name=self.armature_object_name
735
+ )
736
+
737
+ return {"FINISHED"}
738
+
739
+ def invoke(self, context: Context, _event: Event) -> set[str]:
740
+ if not self.armature_object_name:
741
+ armature_object = search.current_armature(context)
742
+ if armature_object:
743
+ self.armature_object_name = armature_object.name
744
+ self.armature_object_name_candidates.clear()
745
+ for obj in context.blend_data.objects:
746
+ if obj.type != "ARMATURE":
747
+ continue
748
+ candidate = self.armature_object_name_candidates.add()
749
+ candidate.value = obj.name
750
+
751
+ return context.window_manager.invoke_props_dialog(self, width=600)
752
+
753
+ def draw(self, _context: Context) -> None:
754
+ layout = self.layout
755
+ layout.label(
756
+ text="Multiple armatures were found; please select one to export as VRM.",
757
+ icon="ERROR",
758
+ )
759
+
760
+ layout.prop_search(
761
+ self,
762
+ "armature_object_name",
763
+ self,
764
+ "armature_object_name_candidates",
765
+ icon="OUTLINER_OB_ARMATURE",
766
+ text="",
767
+ translate=False,
768
+ )
769
+
770
+ if TYPE_CHECKING:
771
+ # This code is auto generated.
772
+ # To regenerate, run the `uv run tools/property_typing.py` command.
773
+ armature_object_name: str # type: ignore[no-redef]
774
+ armature_object_name_candidates: CollectionPropertyProtocol[ # type: ignore[no-redef]
775
+ StringPropertyGroup
776
+ ]
777
+
778
+
779
+ class WM_OT_vrma_export_prerequisite(Operator):
780
+ bl_label = "VRM Animation Export Prerequisite"
781
+ bl_idname = "wm.vrma_export_prerequisite"
782
+ bl_options: ClassVar = {"REGISTER"}
783
+
784
+ armature_object_name: StringProperty( # type: ignore[valid-type]
785
+ options={"HIDDEN"},
786
+ )
787
+ armature_object_name_candidates: CollectionProperty( # type: ignore[valid-type]
788
+ type=StringPropertyGroup,
789
+ options={"HIDDEN"},
790
+ )
791
+
792
+ @staticmethod
793
+ def detect_errors(context: Context, armature_object_name: str) -> list[str]:
794
+ error_messages: list[str] = []
795
+
796
+ if not armature_object_name:
797
+ armature = search.current_armature(context)
798
+ else:
799
+ armature = context.blend_data.objects.get(armature_object_name)
800
+
801
+ if not armature:
802
+ error_messages.append(pgettext("Armature not found"))
803
+ return error_messages
804
+
805
+ armature_data = armature.data
806
+ if not isinstance(armature_data, Armature):
807
+ error_messages.append(pgettext("Armature not found"))
808
+ return error_messages
809
+
810
+ ext = get_armature_extension(armature_data)
811
+ if get_armature_extension(armature_data).is_vrm1():
812
+ humanoid = ext.vrm1.humanoid
813
+ if not humanoid.human_bones.bones_are_correctly_assigned():
814
+ error_messages.append(pgettext("Please assign required human bones"))
815
+ else:
816
+ error_messages.append(pgettext("Please set the version of VRM to 1.0"))
817
+
818
+ return error_messages
819
+
820
+ def execute(self, _context: Context) -> set[str]:
821
+ return ops.export_scene.vrma(
822
+ "INVOKE_DEFAULT", armature_object_name=self.armature_object_name
823
+ )
824
+
825
+ def invoke(self, context: Context, _event: Event) -> set[str]:
826
+ if not self.armature_object_name:
827
+ armature_object = search.current_armature(context)
828
+ if armature_object:
829
+ self.armature_object_name = armature_object.name
830
+ self.armature_object_name_candidates.clear()
831
+ for obj in context.blend_data.objects:
832
+ if obj.type != "ARMATURE":
833
+ continue
834
+ candidate = self.armature_object_name_candidates.add()
835
+ candidate.value = obj.name
836
+ return context.window_manager.invoke_props_dialog(self, width=800)
837
+
838
+ def draw(self, context: Context) -> None:
839
+ layout = self.layout
840
+
841
+ layout.label(
842
+ text="VRM Animation export requires a VRM 1.0 armature",
843
+ icon="INFO",
844
+ )
845
+
846
+ error_messages = WM_OT_vrma_export_prerequisite.detect_errors(
847
+ context, self.armature_object_name
848
+ )
849
+
850
+ layout.prop_search(
851
+ self,
852
+ "armature_object_name",
853
+ self,
854
+ "armature_object_name_candidates",
855
+ icon="OUTLINER_OB_ARMATURE",
856
+ text="Armature to be exported",
857
+ )
858
+
859
+ if error_messages:
860
+ error_column = layout.box().column(align=True)
861
+ for error_message in error_messages:
862
+ error_column.label(text=error_message, icon="ERROR", translate=False)
863
+
864
+ if not self.armature_object_name:
865
+ armature = search.current_armature(context)
866
+ else:
867
+ armature = context.blend_data.objects.get(self.armature_object_name)
868
+ if armature:
869
+ armature_data = armature.data
870
+ if isinstance(armature_data, Armature):
871
+ ext = get_armature_extension(armature_data)
872
+ if get_armature_extension(armature_data).is_vrm1():
873
+ humanoid = ext.vrm1.humanoid
874
+ if not humanoid.human_bones.bones_are_correctly_assigned():
875
+ WM_OT_vrm_export_human_bones_assignment.draw_vrm1(
876
+ self.layout, armature
877
+ )
878
+
879
+ _draw_help_message(layout)
880
+
881
+ if TYPE_CHECKING:
882
+ # This code is auto generated.
883
+ # To regenerate, run the `uv run tools/property_typing.py` command.
884
+ armature_object_name: str # type: ignore[no-redef]
885
+ armature_object_name_candidates: CollectionPropertyProtocol[ # type: ignore[no-redef]
886
+ StringPropertyGroup
887
+ ]
888
+
889
+
890
+ def _draw_help_message(layout: UILayout) -> None:
891
+ help_message = pgettext(
892
+ "Animations to be exported\n"
893
+ + "- Humanoid bone rotations\n"
894
+ + "- Humanoid hips bone translations\n"
895
+ + "- Expression preview value\n"
896
+ + "- Look At preview target translation\n"
897
+ )
898
+ help_box = layout.box()
899
+ help_column = help_box.column(align=True)
900
+ for index, help_line in enumerate(help_message.splitlines()):
901
+ help_column.label(
902
+ text=help_line,
903
+ translate=False,
904
+ icon="NONE" if index else "INFO",
905
+ )
906
+
907
+ open_op = layout_operator(
908
+ help_column,
909
+ VRM_OT_open_url_in_web_browser,
910
+ icon="URL",
911
+ text="Open help in a Web Browser",
912
+ )
913
+ open_op.url = pgettext("https://vrm-addon-for-blender.info/en-us/animation/")
914
+
915
+
916
+ def _collect_export_objects(
917
+ context: Context,
918
+ armature_object_name: str,
919
+ export_preferences: ExportPreferencesProtocol,
920
+ ) -> tuple[list[Object], list[Object]]:
921
+ export_objects = search.export_objects(
922
+ context,
923
+ armature_object_name,
924
+ export_invisibles=export_preferences.export_invisibles,
925
+ export_only_selections=export_preferences.export_only_selections,
926
+ export_lights=export_preferences.enable_advanced_preferences
927
+ and export_preferences.export_lights,
928
+ )
929
+ armature_objects = [obj for obj in export_objects if obj.type == "ARMATURE"]
930
+ return armature_objects, export_objects