@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,628 @@
1
+ # SPDX-License-Identifier: MIT OR GPL-3.0-or-later
2
+ import secrets
3
+ import string
4
+ from abc import ABC, abstractmethod
5
+ from collections.abc import Generator, Mapping, Sequence
6
+ from contextlib import contextmanager
7
+ from dataclasses import dataclass
8
+ from typing import Optional, TypeVar, Union
9
+
10
+ import bmesh
11
+ import bpy
12
+ from bpy.types import Armature, Constraint, Context, Mesh, NodesModifier, Object
13
+ from mathutils import Vector
14
+
15
+ from ..common import shader
16
+ from ..common.convert import Json
17
+ from ..common.deep import make_json
18
+ from ..common.logger import get_logger
19
+ from ..common.vrm0 import human_bone as vrm0_human_bone
20
+ from ..common.vrm1 import human_bone as vrm1_human_bone
21
+ from ..common.workspace import save_workspace
22
+ from ..editor.extension_accessor import get_armature_extension, get_material_extension
23
+ from ..editor.property_group import BonePropertyGroup, BonePropertyGroupType
24
+ from ..editor.search import MESH_CONVERTIBLE_OBJECT_TYPES
25
+ from ..external import io_scene_gltf2_support
26
+
27
+ HumanBoneSpecification = TypeVar(
28
+ "HumanBoneSpecification",
29
+ vrm0_human_bone.HumanBoneSpecification,
30
+ vrm1_human_bone.HumanBoneSpecification,
31
+ )
32
+
33
+
34
+ _logger = get_logger(__name__)
35
+
36
+
37
+ @dataclass(frozen=True)
38
+ class FlexibleHierarchyBoneSetup:
39
+ original_bone_settings: Mapping[str, tuple[Optional[str], bool]]
40
+ pose_bone_name_and_muted_constraint_name: list[tuple[str, str]]
41
+
42
+
43
+ class AbstractBaseVrmExporter(ABC):
44
+ def __init__(
45
+ self,
46
+ context: Context,
47
+ export_objects: Sequence[Object],
48
+ armature: Object,
49
+ ) -> None:
50
+ self._context = context
51
+ self._export_objects = export_objects
52
+ self._armature = armature
53
+ self._export_id = "BlenderVrmAddonExport" + (
54
+ "".join(secrets.choice(string.digits) for _ in range(15))
55
+ )
56
+ self._gltf2_addon_export_settings = (
57
+ io_scene_gltf2_support.create_export_settings()
58
+ )
59
+
60
+ armature_data = self._armature.data
61
+ if not isinstance(armature_data, Armature):
62
+ message = f"{type(armature_data)} is not an Armature"
63
+ raise TypeError(message)
64
+
65
+ self.generator_postfix = ""
66
+
67
+ @abstractmethod
68
+ def export(self) -> Optional[bytes]:
69
+ pass
70
+
71
+ @staticmethod
72
+ def enter_clear_blend_shape_proxy_previews(
73
+ context: Context,
74
+ armature_data: Armature,
75
+ ) -> tuple[Sequence[float], Mapping[str, float], Mapping[str, Mapping[str, float]]]:
76
+ saved_key_block_values: dict[str, Mapping[str, float]] = {}
77
+ for mesh in context.blend_data.meshes:
78
+ shape_keys = mesh.shape_keys
79
+ if not shape_keys:
80
+ continue
81
+ saved_key_block_values[mesh.name] = {
82
+ key_block.name: key_block.value for key_block in shape_keys.key_blocks
83
+ }
84
+
85
+ ext = get_armature_extension(armature_data)
86
+
87
+ saved_vrm1_previews: dict[str, float] = {}
88
+ for (
89
+ name,
90
+ expression,
91
+ ) in ext.vrm1.expressions.all_name_to_expression_dict().items():
92
+ saved_vrm1_previews[name] = expression.preview
93
+ expression.preview = 0
94
+
95
+ saved_vrm0_previews: list[float] = []
96
+ for blend_shape_group in ext.vrm0.blend_shape_master.blend_shape_groups:
97
+ saved_vrm0_previews.append(blend_shape_group.preview)
98
+ blend_shape_group.preview = 0
99
+
100
+ return saved_vrm0_previews, saved_vrm1_previews, saved_key_block_values
101
+
102
+ @staticmethod
103
+ def leave_clear_blend_shape_proxy_previews(
104
+ context: Context,
105
+ armature_data: Armature,
106
+ saved_vrm0_previews: Sequence[float],
107
+ saved_vrm1_previews: Mapping[str, float],
108
+ saved_key_block_values: Mapping[str, Mapping[str, float]],
109
+ ) -> None:
110
+ ext = get_armature_extension(armature_data)
111
+
112
+ for blend_shape_group, blend_shape_preview in reversed(
113
+ list(
114
+ zip(ext.vrm0.blend_shape_master.blend_shape_groups, saved_vrm0_previews)
115
+ )
116
+ ):
117
+ blend_shape_group.preview = blend_shape_preview
118
+
119
+ for (
120
+ name,
121
+ expression,
122
+ ) in ext.vrm1.expressions.all_name_to_expression_dict().items():
123
+ expression_preview = saved_vrm1_previews.get(name)
124
+ if expression_preview is not None:
125
+ expression.preview = expression_preview
126
+
127
+ for mesh_name, key_block_name_to_values in reversed(
128
+ list(saved_key_block_values.items())
129
+ ):
130
+ mesh = context.blend_data.meshes.get(mesh_name)
131
+ if not mesh:
132
+ continue
133
+
134
+ shape_keys = mesh.shape_keys
135
+ if not shape_keys:
136
+ continue
137
+
138
+ for key_block in shape_keys.key_blocks:
139
+ shape_key_value = key_block_name_to_values.get(key_block.name)
140
+ if shape_key_value is not None:
141
+ key_block.value = shape_key_value
142
+
143
+ @contextmanager
144
+ def clear_blend_shape_proxy_previews(
145
+ self, context: Context, armature_data: Armature
146
+ ) -> Generator[None]:
147
+ saved_vrm0_previews, saved_vrm1_previews, saved_key_block_values = (
148
+ self.enter_clear_blend_shape_proxy_previews(context, armature_data)
149
+ )
150
+ try:
151
+ yield
152
+ # After yield, native bpy objects may be deleted or frames may advance,
153
+ # becoming invalid. Accessing them in this state causes crashes, so
154
+ # be careful not to access potentially invalid native objects after yield
155
+ finally:
156
+ self.leave_clear_blend_shape_proxy_previews(
157
+ context,
158
+ armature_data,
159
+ saved_vrm0_previews,
160
+ saved_vrm1_previews,
161
+ saved_key_block_values,
162
+ )
163
+
164
+ def enter_enable_deform_for_all_referenced_bones(
165
+ self, armature_data: Armature
166
+ ) -> list[str]:
167
+ ext = get_armature_extension(armature_data)
168
+ modified_non_deform_bone_names = list[str]()
169
+ for (
170
+ bone_property_group,
171
+ bone_property_group_type,
172
+ ) in BonePropertyGroup.get_all_bone_property_groups(armature_data):
173
+ bone = armature_data.bones.get(bone_property_group.bone_name)
174
+ if not bone or bone.use_deform:
175
+ continue
176
+ if (
177
+ ext.is_vrm0()
178
+ and BonePropertyGroupType.is_vrm0(bone_property_group_type)
179
+ ) or (
180
+ ext.is_vrm1()
181
+ and BonePropertyGroupType.is_vrm1(bone_property_group_type)
182
+ ):
183
+ bone.use_deform = True
184
+ modified_non_deform_bone_names.append(bone.name)
185
+
186
+ if ext.is_vrm1() and bpy.app.version >= (4, 4):
187
+ # Enable deformation for root bone when Blender 4.4+ and VRM 1.0.
188
+ #
189
+ # For VRM 1.0, export is done using the glTF 2.0 add-on, but export
190
+ # becomes unstable when root bone deformation is not present.
191
+ # Compatibility for versions before Blender 4.4 is already handled
192
+ # separately, so here we enable root bone deformation only for 4.4+.
193
+ # https://github.com/KhronosGroup/glTF-Blender-IO/issues/2697
194
+ # https://github.com/saturday06/VRM-Addon-for-Blender/issues/1244
195
+ for bone in armature_data.bones:
196
+ if bone.parent:
197
+ continue
198
+ if bone.use_deform:
199
+ continue
200
+ bone.use_deform = True
201
+ modified_non_deform_bone_names.append(bone.name)
202
+
203
+ return modified_non_deform_bone_names
204
+
205
+ def leave_enable_deform_for_all_referenced_bones(
206
+ self, armature_data: Armature, modified_non_deform_bone_names: list[str]
207
+ ) -> None:
208
+ for modified_non_deform_bone_name in reversed(modified_non_deform_bone_names):
209
+ bone = armature_data.bones.get(modified_non_deform_bone_name)
210
+ if bone and bone.use_deform:
211
+ bone.use_deform = False
212
+
213
+ @contextmanager
214
+ def enable_deform_for_all_referenced_bones(
215
+ self, armature_data: Armature
216
+ ) -> Generator[None]:
217
+ modified_non_deform_bone_names = (
218
+ self.enter_enable_deform_for_all_referenced_bones(armature_data)
219
+ )
220
+ try:
221
+ yield
222
+ finally:
223
+ self.leave_enable_deform_for_all_referenced_bones(
224
+ armature_data, modified_non_deform_bone_names
225
+ )
226
+
227
+ @staticmethod
228
+ def enter_hide_mtoon1_outline_geometry_nodes(
229
+ context: Context,
230
+ ) -> dict[str, list[tuple[str, bool, bool]]]:
231
+ object_name_to_modifiers: dict[str, list[tuple[str, bool, bool]]] = {}
232
+ for obj in context.blend_data.objects:
233
+ for modifier in obj.modifiers:
234
+ if not modifier.show_viewport:
235
+ continue
236
+ if modifier.type != "NODES":
237
+ continue
238
+ if not isinstance(modifier, NodesModifier):
239
+ continue
240
+ node_group = modifier.node_group
241
+ if (
242
+ not node_group
243
+ or node_group.name != shader.OUTLINE_GEOMETRY_GROUP_NAME
244
+ ):
245
+ continue
246
+ modifiers = object_name_to_modifiers.get(obj.name)
247
+ if modifiers is None:
248
+ modifiers = []
249
+ object_name_to_modifiers[obj.name] = modifiers
250
+ modifiers = object_name_to_modifiers[obj.name]
251
+ modifiers.append(
252
+ (
253
+ modifier.name,
254
+ modifier.show_render,
255
+ modifier.show_viewport,
256
+ )
257
+ )
258
+ if modifier.show_render:
259
+ modifier.show_render = False
260
+ if modifier.show_viewport:
261
+ modifier.show_viewport = False
262
+ return object_name_to_modifiers
263
+
264
+ @staticmethod
265
+ def exit_hide_mtoon1_outline_geometry_nodes(
266
+ context: Context,
267
+ object_name_to_modifiers: dict[str, list[tuple[str, bool, bool]]],
268
+ ) -> None:
269
+ for object_name, modifiers in reversed(list(object_name_to_modifiers.items())):
270
+ for modifier_name, render, viewport in reversed(modifiers):
271
+ obj = context.blend_data.objects.get(object_name)
272
+ if not obj:
273
+ continue
274
+ modifier = obj.modifiers.get(modifier_name)
275
+ if (
276
+ not modifier
277
+ or modifier.type != "NODES"
278
+ or not isinstance(modifier, NodesModifier)
279
+ ):
280
+ continue
281
+ node_group = modifier.node_group
282
+ if (
283
+ not node_group
284
+ or node_group.name != shader.OUTLINE_GEOMETRY_GROUP_NAME
285
+ ):
286
+ continue
287
+ if modifier.show_render != render:
288
+ modifier.show_render = render
289
+ if modifier.show_viewport != viewport:
290
+ modifier.show_viewport = viewport
291
+
292
+ @staticmethod
293
+ @contextmanager
294
+ def hide_mtoon1_outline_geometry_nodes(context: Context) -> Generator[None]:
295
+ object_name_to_modifier_names = (
296
+ AbstractBaseVrmExporter.enter_hide_mtoon1_outline_geometry_nodes(context)
297
+ )
298
+ try:
299
+ yield
300
+ # After yield, native bpy objects may be deleted or frames may advance,
301
+ # becoming invalid. Accessing them in this state causes crashes, so
302
+ # be careful not to access potentially invalid native objects after yield
303
+ finally:
304
+ AbstractBaseVrmExporter.exit_hide_mtoon1_outline_geometry_nodes(
305
+ context, object_name_to_modifier_names
306
+ )
307
+
308
+ @staticmethod
309
+ def create_child_bone_name_to_parent_bone_name(
310
+ armature_data: Armature,
311
+ assigned_human_bone_specification_to_bone_name: Mapping[
312
+ HumanBoneSpecification, str
313
+ ],
314
+ all_human_bone_specifications: Sequence[HumanBoneSpecification],
315
+ ) -> Mapping[str, str]:
316
+ assigned_bone_names = set(
317
+ assigned_human_bone_specification_to_bone_name.values()
318
+ )
319
+ child_bone_name_to_parent_bone_name: dict[str, str] = {}
320
+ for human_bone_specification in all_human_bone_specifications:
321
+ child_bone_name = assigned_human_bone_specification_to_bone_name.get(
322
+ human_bone_specification
323
+ )
324
+ if not child_bone_name:
325
+ continue
326
+ child_bone = armature_data.bones.get(child_bone_name)
327
+ if not child_bone:
328
+ continue
329
+ ancestor = human_bone_specification.parent
330
+ while ancestor:
331
+ ancestor_bone_name = assigned_human_bone_specification_to_bone_name.get(
332
+ ancestor
333
+ )
334
+ if not ancestor_bone_name:
335
+ if ancestor.requirement:
336
+ break
337
+ ancestor = ancestor.parent
338
+ continue
339
+
340
+ search_bone = child_bone.parent
341
+ while search_bone:
342
+ if search_bone.name in assigned_bone_names:
343
+ break
344
+ search_bone = search_bone.parent
345
+ if search_bone and search_bone.name == ancestor_bone_name:
346
+ break
347
+
348
+ child_bone_name_to_parent_bone_name[child_bone_name] = (
349
+ ancestor_bone_name
350
+ )
351
+ break
352
+
353
+ return child_bone_name_to_parent_bone_name
354
+
355
+ @staticmethod
356
+ def reparent_edit_bones(
357
+ context: Context,
358
+ armature_object: Object,
359
+ assigned_bone_names: Mapping[HumanBoneSpecification, str],
360
+ child_bone_name_to_parent_bone_name: Mapping[str, str],
361
+ ) -> Mapping[str, tuple[Optional[str], bool]]:
362
+ if not isinstance(armature_data := armature_object.data, Armature):
363
+ return {}
364
+ original_bone_settings: dict[str, tuple[Optional[str], bool]] = {}
365
+ with save_workspace(context, armature_object, mode="EDIT"):
366
+ for bone_name in assigned_bone_names.values():
367
+ edit_bone = armature_data.edit_bones.get(bone_name)
368
+ if not edit_bone:
369
+ continue
370
+ original_bone_settings[bone_name] = (
371
+ edit_bone.parent.name if edit_bone.parent else None,
372
+ edit_bone.use_connect,
373
+ )
374
+ edit_bone.use_connect = False
375
+
376
+ for (
377
+ child_bone_name,
378
+ ancestor_bone_name,
379
+ ) in child_bone_name_to_parent_bone_name.items():
380
+ child_edit_bone = armature_data.edit_bones.get(child_bone_name)
381
+ parent_edit_bone = armature_data.edit_bones.get(ancestor_bone_name)
382
+ if not child_edit_bone or not parent_edit_bone:
383
+ continue
384
+ child_edit_bone.parent = parent_edit_bone
385
+
386
+ return original_bone_settings
387
+
388
+ @staticmethod
389
+ def enter_setup_flexible_hierarchy_bones(
390
+ _context: Context,
391
+ _armature_object: Object,
392
+ _export_objects: Sequence[Object],
393
+ ) -> Optional[FlexibleHierarchyBoneSetup]:
394
+ """Build the flexible hierarchy setup when a subclass supports it.
395
+
396
+ Subclasses can override this to build the assigned-bone map and call
397
+ ``reparent_edit_bones``. The default returns ``None``.
398
+ """
399
+ return None
400
+
401
+ @staticmethod
402
+ def leave_setup_flexible_hierarchy_bones(
403
+ context: Context,
404
+ armature_object: Object,
405
+ flexible_hierarchy_bone_setup: FlexibleHierarchyBoneSetup,
406
+ ) -> None:
407
+ """Restore bone parents to the saved state.
408
+
409
+ Use the state recorded by ``enter_setup_flexible_hierarchy_bones``.
410
+ """
411
+ armature_data = armature_object.data
412
+ if not isinstance(armature_data, Armature):
413
+ return
414
+
415
+ with save_workspace(context, armature_object, mode="EDIT"):
416
+ for (
417
+ bone_name
418
+ ) in flexible_hierarchy_bone_setup.original_bone_settings.keys():
419
+ edit_bone = armature_data.edit_bones.get(bone_name)
420
+ if edit_bone:
421
+ edit_bone.parent = None
422
+ edit_bone.use_connect = False
423
+
424
+ for bone_name, (
425
+ parent_name,
426
+ use_connect,
427
+ ) in flexible_hierarchy_bone_setup.original_bone_settings.items():
428
+ edit_bone = armature_data.edit_bones.get(bone_name)
429
+ if not edit_bone:
430
+ continue
431
+ if parent_name:
432
+ parent_edit_bone = armature_data.edit_bones.get(parent_name)
433
+ if parent_edit_bone:
434
+ edit_bone.parent = parent_edit_bone
435
+ edit_bone.use_connect = use_connect
436
+
437
+ for pose_bone_name, muted_constraint_name in reversed(
438
+ flexible_hierarchy_bone_setup.pose_bone_name_and_muted_constraint_name
439
+ ):
440
+ pose_bone = armature_object.pose.bones.get(pose_bone_name)
441
+ if not pose_bone:
442
+ continue
443
+ constraint = pose_bone.constraints.get(muted_constraint_name)
444
+ if isinstance(constraint, Constraint) and constraint.mute:
445
+ constraint.mute = False
446
+
447
+ @classmethod
448
+ @contextmanager
449
+ def setup_flexible_hierarchy_bones(
450
+ cls,
451
+ context: Context,
452
+ armature: Object,
453
+ export_objects: Sequence[Object],
454
+ ) -> Generator[None]:
455
+ flexible_hierarchy_bone_setup = cls.enter_setup_flexible_hierarchy_bones(
456
+ context,
457
+ armature,
458
+ export_objects,
459
+ )
460
+ try:
461
+ yield
462
+ finally:
463
+ if flexible_hierarchy_bone_setup is not None:
464
+ cls.leave_setup_flexible_hierarchy_bones(
465
+ context,
466
+ armature,
467
+ flexible_hierarchy_bone_setup,
468
+ )
469
+
470
+ @staticmethod
471
+ def setup_mtoon_gltf_fallback_nodes(context: Context, *, is_vrm0: bool) -> None:
472
+ """Reflect MToon node values to nodes used for glTF fallback values.
473
+
474
+ When MToon nodes are directly edited, glTF fallback values are not
475
+ automatically set. Therefore, we explicitly set values during export.
476
+ """
477
+ for material in context.blend_data.materials:
478
+ mtoon1 = get_material_extension(material).mtoon1
479
+ if not mtoon1.enabled:
480
+ continue
481
+ mtoon1.pbr_metallic_roughness.base_color_factor = (
482
+ mtoon1.pbr_metallic_roughness.base_color_factor
483
+ )
484
+ mtoon1.emissive_factor = mtoon1.emissive_factor
485
+ mtoon1.normal_texture.scale = mtoon1.normal_texture.scale
486
+ mtoon1.extensions.khr_materials_emissive_strength.emissive_strength = (
487
+ mtoon1.extensions.khr_materials_emissive_strength.emissive_strength
488
+ )
489
+ for texture in mtoon1.all_textures(downgrade_to_mtoon0=is_vrm0):
490
+ texture.source = texture.get_connected_node_image()
491
+
492
+
493
+ def assign_dict(
494
+ target: dict[str, Json],
495
+ key: str,
496
+ value: Union[Json, tuple[float, float, float], tuple[float, float, float, float]],
497
+ default_value: Json = None,
498
+ ) -> bool:
499
+ if value is None or value == default_value:
500
+ return False
501
+ target[key] = make_json(value)
502
+ return True
503
+
504
+
505
+ def generate_evaluated_mesh(context: Context, obj: Object) -> Optional[Mesh]:
506
+ obj_data = obj.data
507
+ if obj_data is None:
508
+ return None
509
+
510
+ # https://docs.blender.org/api/2.80/Depsgraph.html
511
+ # TODO: Shape keys may sometimes break
512
+ depsgraph = context.evaluated_depsgraph_get()
513
+ evaluated_obj = obj.evaluated_get(depsgraph)
514
+ evaluated_temporary_mesh = evaluated_obj.to_mesh(
515
+ preserve_all_data_layers=True, depsgraph=depsgraph
516
+ )
517
+ if not evaluated_temporary_mesh:
518
+ return None
519
+
520
+ # The documentation says to use BlendDataMeshes.new_from_object(), but
521
+ # that doesn't preserve shape keys.
522
+ if isinstance(obj_data, Mesh):
523
+ evaluated_mesh = obj_data.copy()
524
+ else:
525
+ _logger.error(
526
+ "Unexpected object type: %s name=%s",
527
+ type(obj_data),
528
+ obj_data.name,
529
+ )
530
+ evaluated_mesh = context.blend_data.meshes.new(name=obj_data.name)
531
+
532
+ bm = bmesh.new()
533
+ bm.from_mesh(evaluated_temporary_mesh)
534
+ bm.to_mesh(evaluated_mesh)
535
+ bm.free()
536
+
537
+ evaluated_obj.to_mesh_clear()
538
+ return evaluated_mesh
539
+
540
+
541
+ def force_apply_modifiers(context: Context, obj: Object) -> Optional[Mesh]:
542
+ if obj.type not in MESH_CONVERTIBLE_OBJECT_TYPES:
543
+ return None
544
+
545
+ key_block_name_to_value: dict[str, float] = {}
546
+ if isinstance(mesh_data := obj.data, Mesh) and (shape_keys := mesh_data.shape_keys):
547
+ for key_block in shape_keys.key_blocks:
548
+ key_block_name_to_value[key_block.name] = key_block.value
549
+ key_block.value = 0
550
+
551
+ context.view_layer.update()
552
+
553
+ evaluated_mesh_data: Optional[Mesh] = None
554
+ try:
555
+ evaluated_mesh_data = generate_evaluated_mesh(context, obj)
556
+ if evaluated_mesh_data is None:
557
+ return None
558
+
559
+ obj_data = obj.data
560
+ if obj_data is None:
561
+ return evaluated_mesh_data
562
+
563
+ if not isinstance(obj_data, Mesh):
564
+ return evaluated_mesh_data
565
+
566
+ shape_keys = obj_data.shape_keys
567
+ if not shape_keys:
568
+ return evaluated_mesh_data
569
+
570
+ evaluated_mesh_data_shape_keys = evaluated_mesh_data.shape_keys
571
+ if not evaluated_mesh_data_shape_keys:
572
+ return evaluated_mesh_data
573
+
574
+ # If the mesh has shape keys, reproduce them as much as possible
575
+ for key_block in shape_keys.key_blocks:
576
+ evaluated_key_block = evaluated_mesh_data_shape_keys.key_blocks.get(
577
+ key_block.name
578
+ )
579
+ if not evaluated_key_block:
580
+ continue
581
+
582
+ if key_block.name == shape_keys.reference_key.name:
583
+ continue
584
+
585
+ key_block.value = 1.0
586
+ context.view_layer.update()
587
+
588
+ depsgraph = context.evaluated_depsgraph_get()
589
+ baked_key_block_obj = obj.evaluated_get(depsgraph)
590
+ baked_key_block_mesh = baked_key_block_obj.to_mesh(
591
+ preserve_all_data_layers=True, depsgraph=depsgraph
592
+ )
593
+ if baked_key_block_mesh:
594
+ evaluated_key_block_data = evaluated_key_block.data
595
+ baked_key_block_mesh_vertices = baked_key_block_mesh.vertices
596
+
597
+ # TODO: If the number of vertices is different, we should use advanced
598
+ # graph matching algorithm.
599
+ for i in range(
600
+ min(
601
+ len(evaluated_key_block_data),
602
+ len(baked_key_block_mesh_vertices),
603
+ )
604
+ ):
605
+ evaluated_key_block_data[i].co = Vector(
606
+ baked_key_block_mesh_vertices[i].co
607
+ )
608
+
609
+ baked_key_block_obj.to_mesh_clear()
610
+ key_block.value = 0.0
611
+
612
+ return evaluated_mesh_data
613
+ finally:
614
+ for mesh_data in (obj.data, evaluated_mesh_data):
615
+ if not isinstance(mesh_data, Mesh):
616
+ continue
617
+
618
+ shape_keys = mesh_data.shape_keys
619
+ if not shape_keys:
620
+ continue
621
+
622
+ for shape_key_name, shape_key_value in key_block_name_to_value.items():
623
+ key_block = shape_keys.key_blocks.get(shape_key_name)
624
+ if key_block:
625
+ key_block.value = shape_key_value
626
+ context.view_layer.update()
627
+
628
+ return evaluated_mesh_data