@volter/blender-engine 0.1.147 → 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,46 @@
1
+ # SPDX-License-Identifier: MIT OR GPL-3.0-or-later
2
+
3
+ import bpy
4
+ from bpy.types import Context, Object
5
+
6
+ from .logger import get_logger
7
+
8
+ _logger = get_logger(__name__)
9
+
10
+
11
+ def remove_object(context: Context, obj: Object) -> bool:
12
+ for child in tuple(obj.children):
13
+ if child.parent_type != obj.parent_type:
14
+ child.parent_type = obj.parent_type
15
+ if child.parent != obj.parent:
16
+ child.parent = obj.parent
17
+ if child.parent_bone != obj.parent_bone:
18
+ child.parent_bone = obj.parent_bone
19
+
20
+ for collection in tuple(obj.users_collection):
21
+ if obj.name in collection.objects:
22
+ collection.objects.unlink(obj)
23
+
24
+ if obj.parent_type != "OBJECT":
25
+ obj.parent_type = "OBJECT"
26
+ if obj.parent is not None:
27
+ obj.parent = None
28
+
29
+ if obj.users:
30
+ user_class_names = list[str]()
31
+ _logger.debug("Object %r is used by %d users:", obj.name, obj.users)
32
+ for users in context.blend_data.user_map(subset=(obj,)).values():
33
+ for user in users:
34
+ user_class_names.append(type(user).__name__)
35
+ _logger.debug(" %r", user)
36
+
37
+ # TODO: In Blender 5.2, Scene objects may remain as users.
38
+ # Until the cause is identified, forcibly remove with do_unlink=True.
39
+ if bpy.app.version >= (5, 2) and user_class_names == ["Scene"]:
40
+ context.blend_data.objects.remove(obj, do_unlink=True)
41
+ return True
42
+
43
+ return False
44
+
45
+ context.blend_data.objects.remove(obj, do_unlink=True)
46
+ return True
@@ -0,0 +1,230 @@
1
+ # SPDX-License-Identifier: MIT OR GPL-3.0-or-later
2
+ import base64
3
+ import hashlib
4
+ import inspect
5
+ from collections.abc import Sequence
6
+ from dataclasses import dataclass, field
7
+ from enum import Enum
8
+ from pathlib import Path
9
+ from typing import Final, Optional, Protocol
10
+
11
+ import bpy
12
+ from bpy.app.handlers import persistent
13
+ from bpy.types import Context
14
+
15
+
16
+ class RunState(Enum):
17
+ PREEMPT = 1
18
+ FINISH = 2
19
+
20
+
21
+ class SceneWatcher(Protocol):
22
+ """Protocol for detecting scene changes and performing some action when detected."""
23
+
24
+ def run(self, context: Context) -> RunState:
25
+ """Detect scene changes and perform some action when detected.
26
+
27
+ If no changes that need to be detected have occurred in the scene, this method
28
+ must return in less than 100 microseconds. If there is not enough time, save
29
+ the current state to instance variables and interrupt the process.
30
+
31
+ This method is executed multiple times across Blender frames. Usually such
32
+ processing can be efficiently implemented using generators or async, but this
33
+ class does not do so.
34
+
35
+ The reason is that references to Blender's native objects cannot be held
36
+ across frames, but programming with generators and async is difficult when
37
+ considering this limitation.
38
+
39
+ :return FINISH if processing is complete. PREEMPT if not complete.
40
+ """
41
+ raise NotImplementedError
42
+
43
+ def create_fast_path_performance_test_objects(self, context: Context) -> None:
44
+ """Create objects for testing whether run() normally completes.
45
+
46
+ This tests whether the run() method normally completes in less than 100
47
+ microseconds.
48
+ """
49
+ raise NotImplementedError
50
+
51
+ def reset_run_progress(self) -> None:
52
+ """Reset the interrupted state of run()."""
53
+ raise NotImplementedError
54
+
55
+
56
+ @dataclass
57
+ class SceneWatcherSchedule:
58
+ """Holds SceneWatcher and its operation status."""
59
+
60
+ scene_watcher: Final[SceneWatcher]
61
+ requires_run_once_more: bool = False
62
+ finished: bool = False
63
+
64
+
65
+ @dataclass
66
+ class SceneWatcherScheduler:
67
+ """Periodically executes registered SceneWatchers in order.
68
+
69
+ This scheduler is careful not to block the UI while executing the
70
+ SceneWatcher instances.
71
+ """
72
+
73
+ INTERVAL: Final[float] = 0.2
74
+ scene_watcher_schedule_index: int = 0
75
+ scene_watcher_type_to_schedule: Final[
76
+ dict[type[SceneWatcher], SceneWatcherSchedule]
77
+ ] = field(default_factory=dict[type[SceneWatcher], SceneWatcherSchedule])
78
+ scene_watcher_schedules: Final[list[SceneWatcherSchedule]] = field(
79
+ default_factory=list[SceneWatcherSchedule]
80
+ )
81
+
82
+ def trigger(self, scene_watcher_type: type[SceneWatcher]) -> None:
83
+ scene_watcher_schedule = self.scene_watcher_type_to_schedule.get(
84
+ scene_watcher_type
85
+ )
86
+ if scene_watcher_schedule:
87
+ # High-frequency fast path.
88
+ # Allow returning with minimal processing.
89
+
90
+ if scene_watcher_schedule.finished:
91
+ # If the task is finished, restart it
92
+ scene_watcher_schedule.finished = False
93
+ scene_watcher_schedule.scene_watcher.reset_run_progress()
94
+ else:
95
+ # If the task is running,
96
+ # set it to "run once more after completion"
97
+ scene_watcher_schedule.requires_run_once_more = True
98
+ return
99
+
100
+ # Low-frequency slow path.
101
+ # Heavy processing is okay.
102
+
103
+ # To prevent unregistered SceneWatchers from being missed in testing,
104
+ # throw an error if trying to instantiate an unregistered SceneWatcher.
105
+ if scene_watcher_type not in self.get_all_scene_watcher_types():
106
+ message = f"{scene_watcher_type} is not registered"
107
+ raise NotImplementedError(message)
108
+
109
+ new_scene_watcher = scene_watcher_type()
110
+ new_schedule = SceneWatcherSchedule(scene_watcher=new_scene_watcher)
111
+ self.scene_watcher_type_to_schedule[scene_watcher_type] = new_schedule
112
+ self.scene_watcher_schedules.append(new_schedule)
113
+
114
+ @staticmethod
115
+ def get_all_scene_watcher_types() -> Sequence[type[SceneWatcher]]:
116
+ from ..editor.mtoon1.scene_watcher import MToon1AutoSetup, OutlineUpdater
117
+ from ..editor.vrm1.scene_watcher import LookAtPreviewUpdater
118
+
119
+ return [OutlineUpdater, LookAtPreviewUpdater, MToon1AutoSetup]
120
+
121
+ def process(self, context: Context) -> bool:
122
+ """Execute one SceneWatcher.
123
+
124
+ Use index values to reduce GC allocation.
125
+ """
126
+ if not self.scene_watcher_schedules:
127
+ return False
128
+
129
+ for _ in range(len(self.scene_watcher_schedules)):
130
+ self.scene_watcher_schedule_index += 1
131
+ self.scene_watcher_schedule_index %= len(self.scene_watcher_schedules)
132
+
133
+ # Skip tasks that are already completed
134
+ scene_watcher_schedule = self.scene_watcher_schedules[
135
+ self.scene_watcher_schedule_index
136
+ ]
137
+ if scene_watcher_schedule.finished:
138
+ continue
139
+
140
+ # Execute the task
141
+ run_state = scene_watcher_schedule.scene_watcher.run(context)
142
+ if run_state == RunState.FINISH:
143
+ if scene_watcher_schedule.requires_run_once_more:
144
+ scene_watcher_schedule.requires_run_once_more = False
145
+ scene_watcher_schedule.scene_watcher.reset_run_progress()
146
+ else:
147
+ scene_watcher_schedule.finished = True
148
+
149
+ # Return if at least one task was executed
150
+ return True
151
+ return False
152
+
153
+ def flush(self, context: Context) -> None:
154
+ while self.process(context):
155
+ pass
156
+
157
+
158
+ _scene_watcher_scheduler: Final = SceneWatcherScheduler()
159
+
160
+
161
+ def process_scene_watcher_scheduler() -> Optional[float]:
162
+ context = bpy.context
163
+
164
+ _scene_watcher_scheduler.process(context)
165
+ return SceneWatcherScheduler.INTERVAL
166
+
167
+
168
+ def create_fast_path_performance_test_scene(
169
+ context: Context, scene_watcher: SceneWatcher
170
+ ) -> None:
171
+ class_file_path_str = inspect.getfile(type(scene_watcher))
172
+ if not class_file_path_str:
173
+ message = f"No path for class {type(scene_watcher)}"
174
+ raise ValueError(message)
175
+ class_file_path = Path(class_file_path_str)
176
+ if not class_file_path.exists():
177
+ message = f"No {class_file_path} found"
178
+ raise ValueError(message)
179
+ class_source_hash = (
180
+ base64.urlsafe_b64encode(
181
+ hashlib.sha3_224(class_file_path.read_bytes()).digest()
182
+ )
183
+ .rstrip(b"=")
184
+ .decode()
185
+ )
186
+ repository_root_path = (
187
+ Path(__file__).resolve(strict=True).parent.parent.parent.parent
188
+ )
189
+ expected_scene_watcher_source_path = (
190
+ repository_root_path / "src" / "io_scene_vrm" / "common" / "scene_watcher.py"
191
+ )
192
+
193
+ if (
194
+ not expected_scene_watcher_source_path.exists()
195
+ or (
196
+ expected_scene_watcher_source_path.resolve(strict=True)
197
+ != Path(__file__).resolve(strict=True)
198
+ )
199
+ or not (repository_root_path / ".git").exists()
200
+ or not (repository_root_path / "pyproject.toml").exists()
201
+ ):
202
+ message = f"Repository root not found at {repository_root_path}"
203
+ raise AssertionError(message)
204
+
205
+ cache_folder_path = repository_root_path / ".local" / "var" / "cache"
206
+ cached_blend_path = cache_folder_path / (
207
+ type(scene_watcher).__name__
208
+ + "-"
209
+ + "_".join(map(str, bpy.app.version))
210
+ + "-"
211
+ + class_source_hash
212
+ + ".blend"
213
+ )
214
+ if not cached_blend_path.exists():
215
+ bpy.ops.wm.read_homefile(use_empty=True)
216
+ scene_watcher.create_fast_path_performance_test_objects(context)
217
+ bpy.ops.wm.save_as_mainfile(filepath=str(cached_blend_path))
218
+ bpy.ops.wm.read_homefile(use_empty=True)
219
+ bpy.ops.wm.open_mainfile(filepath=str(cached_blend_path))
220
+
221
+
222
+ def trigger_scene_watcher(scene_watcher_type: type[SceneWatcher]) -> None:
223
+ _scene_watcher_scheduler.trigger(scene_watcher_type)
224
+
225
+
226
+ @persistent
227
+ def save_pre(_unused: object) -> None:
228
+ context = bpy.context
229
+
230
+ _scene_watcher_scheduler.flush(context)