@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,162 @@
1
+ # SPDX-License-Identifier: MIT OR GPL-3.0-or-later
2
+ import difflib
3
+ import math
4
+ from collections.abc import Mapping
5
+ from json import dumps as json_dumps
6
+ from types import MappingProxyType
7
+
8
+ from . import convert
9
+ from .convert import Json, JsonView
10
+ from .logger import get_logger
11
+
12
+ _logger = get_logger(__name__)
13
+
14
+
15
+ def make_json(v: object) -> Json:
16
+ if v is None:
17
+ return None
18
+ if isinstance(v, int):
19
+ return v
20
+ if isinstance(v, float):
21
+ if not math.isfinite(v):
22
+ _logger.warning("%s %s is non-finite value", v, type(v))
23
+ return 0.0
24
+ return v
25
+ if isinstance(v, bool):
26
+ return v
27
+ if isinstance(v, str):
28
+ return v
29
+
30
+ mapping = convert.mapping_or_none(v)
31
+ if mapping is not None:
32
+ dict_result: dict[str, Json] = {}
33
+ for key, value in mapping.items():
34
+ if isinstance(key, str):
35
+ dict_result[key] = make_json(value)
36
+ continue
37
+ _logger.warning("%s %s is unrecognized type for dict key", key, type(key))
38
+ return dict_result
39
+
40
+ iterator = convert.iterator_or_none(v)
41
+ if iterator is not None:
42
+ return [make_json(x) for x in iterator]
43
+
44
+ _logger.warning("%s %s is unrecognized type", v, type(v))
45
+ return None
46
+
47
+
48
+ def make_json_view(v: object) -> JsonView:
49
+ json = make_json(v)
50
+ if isinstance(json, dict):
51
+ return MappingProxyType(
52
+ {key: make_json_view(value) for key, value in json.items()}
53
+ )
54
+ if isinstance(json, list):
55
+ return tuple(make_json_view(x) for x in json)
56
+ return json
57
+
58
+
59
+ def make_json_dict(v: Mapping[str, object]) -> dict[str, Json]:
60
+ return {key: make_json(value) for key, value in v.items()}
61
+
62
+
63
+ def diff(
64
+ left: Json,
65
+ right: Json,
66
+ float_tolerance: float = 0,
67
+ path: str = "",
68
+ ) -> list[str]:
69
+ if isinstance(left, list):
70
+ if not isinstance(right, list):
71
+ return [f"{path}: left is list but right is {type(right)}"]
72
+ if len(left) != len(right):
73
+ result = [
74
+ f"{path}: left length is {len(left)} but right length is {len(right)}"
75
+ ]
76
+
77
+ right_json_str = json_dumps(right, indent=4, sort_keys=True)
78
+ right_json_lines = right_json_str.splitlines(keepends=True)
79
+ left_json_str = json_dumps(left, indent=4, sort_keys=True)
80
+ left_json_lines = left_json_str.splitlines(keepends=True)
81
+
82
+ if len(right_json_lines) > 1000 or len(left_json_lines) > 1000:
83
+ return result
84
+
85
+ unified_diff = [
86
+ line.rstrip()
87
+ for line in difflib.unified_diff(
88
+ right_json_lines,
89
+ left_json_lines,
90
+ f"{path}/right",
91
+ f"{path}/left",
92
+ )
93
+ ]
94
+ if len(unified_diff) > 1000:
95
+ return result
96
+ return result + unified_diff
97
+ diffs: list[str] = []
98
+ for i, (left_child, right_child) in enumerate(zip(left, right)):
99
+ diffs.extend(diff(left_child, right_child, float_tolerance, f"{path}[{i}]"))
100
+ return diffs
101
+
102
+ if isinstance(left, dict):
103
+ if not isinstance(right, dict):
104
+ return [f"{path}: left is dict but right is {type(right)}"]
105
+ diffs = []
106
+ for key in sorted(left.keys() | right.keys()):
107
+ if key not in left:
108
+ diffs.append(f'{path}: {key} not in left. right["{key}"]={right[key]}')
109
+ continue
110
+ if key not in right:
111
+ diffs.append(f'{path}: {key} not in right, left["{key}"]={left[key]}')
112
+ continue
113
+ diffs.extend(
114
+ diff(left[key], right[key], float_tolerance, f'{path}["{key}"]')
115
+ )
116
+ return diffs
117
+
118
+ if isinstance(left, bool):
119
+ if not isinstance(right, bool):
120
+ return [f"{path}: left is bool but right is {type(right)}"]
121
+ if left != right:
122
+ return [f"{path}: left is {left} but right is {right}"]
123
+ return []
124
+
125
+ if isinstance(left, str):
126
+ if not isinstance(right, str):
127
+ return [f"{path}: left is str but right is {type(right)}"]
128
+ if left != right:
129
+ return [f'{path}: left is "{left}" but right is "{right}"']
130
+ return []
131
+
132
+ if isinstance(left, int) and isinstance(right, int):
133
+ if left != right:
134
+ return [f"{path}: left is {left} but right is {right}"]
135
+ return []
136
+
137
+ if isinstance(left, (int, float)) and isinstance(right, (int, float)):
138
+ if (isinstance(left, float) and not math.isfinite(left)) or (
139
+ isinstance(right, float) and not math.isfinite(right)
140
+ ):
141
+ return [
142
+ f"{path}: left is {left}"
143
+ + f" but right is {right}, They are not comparable numbers"
144
+ ]
145
+
146
+ error = math.fabs(float(left) - float(right))
147
+ if error > float_tolerance:
148
+ return [
149
+ f"{path}: left is {float(left):20.17f}"
150
+ + f" but right is {float(right):20.17f}, error={error:19.17f}"
151
+ ]
152
+ return []
153
+
154
+ if left is None:
155
+ if right is None:
156
+ return []
157
+ return [f"{path}: left is None but right is {type(right)}"]
158
+ if right is None:
159
+ return [f"{path}: left is {type(left)} but right is None"]
160
+
161
+ message = f"{path}: unexpected type left={type(left)} right={type(right)}"
162
+ raise ValueError(message)
@@ -0,0 +1,274 @@
1
+ # SPDX-License-Identifier: MIT OR GPL-3.0-or-later
2
+
3
+ import contextlib
4
+ import datetime as dt
5
+ import getpass
6
+ import platform
7
+ import sys
8
+ import sysconfig
9
+ from collections.abc import Sequence
10
+ from pathlib import Path
11
+ from typing import TYPE_CHECKING, ClassVar, Union
12
+
13
+ import bpy
14
+ from bpy.props import CollectionProperty, IntProperty, StringProperty
15
+ from bpy.types import (
16
+ Context,
17
+ Event,
18
+ Operator,
19
+ PropertyGroup,
20
+ UILayout,
21
+ UIList,
22
+ )
23
+ from bpy_extras.io_utils import ExportHelper
24
+
25
+ from ..common import convert, ops
26
+ from ..common.logger import get_logger
27
+ from ..common.version import get_addon_version
28
+ from ..editor.ops import VRM_OT_open_url_in_web_browser, layout_operator
29
+ from ..editor.property_group import CollectionPropertyProtocol
30
+ from ..external import io_scene_gltf2_support
31
+
32
+ _logger = get_logger(__name__)
33
+
34
+
35
+ def show_error_dialog(
36
+ title: str,
37
+ lines: Union[str, Sequence[str]],
38
+ *,
39
+ append_environment: bool = True,
40
+ ) -> set[str]:
41
+ if isinstance(lines, str):
42
+ lines = [line.rstrip() for line in lines.splitlines()]
43
+ while lines and not lines[0].strip():
44
+ del lines[0]
45
+ while lines and not lines[-1].strip():
46
+ del lines[-1]
47
+ else:
48
+ lines = list(lines)
49
+ if not lines:
50
+ return {"CANCELLED"}
51
+ if append_environment:
52
+ if lines:
53
+ lines[0] = f"Python: {lines[0]}"
54
+ lines = io_scene_gltf2_support.read_addon_diagnostics() + lines
55
+
56
+ os_name = None
57
+ platform_system, _, _ = platform.system_alias(
58
+ platform.system(), platform.release(), platform.version()
59
+ )
60
+ if sys.version_info >= (3, 10) and platform_system not in {"Windows", "Darwin"}:
61
+ # https://github.com/python/cpython/blob/v3.10.18/Doc/library/platform.rst?plain=1#L271-L272
62
+ with contextlib.suppress(OSError):
63
+ os_name = platform.freedesktop_os_release().get("PRETTY_NAME")
64
+
65
+ if not os_name:
66
+ os_name = platform_system
67
+
68
+ python_version = (
69
+ platform.python_implementation()
70
+ + " "
71
+ + ".".join(map(str, sys.version_info[:3]))
72
+ )
73
+ runtime_platform = os_name + " " + platform.machine()
74
+ build_platform = sysconfig.get_platform()
75
+ lines.insert(
76
+ 0,
77
+ "Environment: Blender {} / VRM Add-on {} / {} / {} ({})".format(
78
+ bpy.app.version_string,
79
+ ".".join(map(str, get_addon_version())),
80
+ python_version,
81
+ runtime_platform,
82
+ build_platform,
83
+ ),
84
+ )
85
+ return ops.wm.vrm_error_dialog(
86
+ "INVOKE_DEFAULT",
87
+ title=title,
88
+ lines=[
89
+ {
90
+ "name": f"error_line_{i}",
91
+ "line": _mask_private_string(line),
92
+ }
93
+ for i, line in enumerate(lines)
94
+ ],
95
+ )
96
+
97
+
98
+ def _mask_private_string(message: str) -> str:
99
+ script_path = Path(__file__).parent.parent.parent.parent.parent
100
+ if len(script_path.parts) > 3:
101
+ message = message.replace(str(script_path), "<script path>")
102
+
103
+ message = message.replace(str(Path.home()), "<home path>")
104
+ message = message.replace(getpass.getuser(), "<user name>")
105
+ binary_path = bpy.app.binary_path
106
+ if binary_path:
107
+ binary_folder_path = Path(binary_path).parent
108
+ message = message.replace(str(binary_folder_path), "<bin folder path>")
109
+ return message
110
+
111
+
112
+ class VrmErrorDialogMessageLine(PropertyGroup):
113
+ line: StringProperty() # type: ignore[valid-type]
114
+
115
+ if TYPE_CHECKING:
116
+ # This code is auto generated.
117
+ # To regenerate, run the `uv run tools/property_typing.py` command.
118
+ line: str # type: ignore[no-redef]
119
+
120
+
121
+ class VRM_UL_vrm_error_dialog_message(UIList):
122
+ bl_idname = "VRM_UL_vrm_error_dialog_message"
123
+
124
+ def filter_items(
125
+ self,
126
+ _context: Context,
127
+ data: object,
128
+ propname: str,
129
+ ) -> tuple[list[int], list[int]]:
130
+ items = convert.sequence_or_none(getattr(data, propname, None))
131
+ if items is None:
132
+ return ([], [])
133
+
134
+ flt_flags: list[int] = [self.bitflag_filter_item] * len(items)
135
+ flt_neworder: list[int] = []
136
+
137
+ filter_name: str = self.filter_name.casefold()
138
+ if not filter_name:
139
+ return (flt_flags, flt_neworder)
140
+
141
+ for index, item in enumerate(items):
142
+ if (
143
+ isinstance(item, VrmErrorDialogMessageLine)
144
+ and filter_name in item.line.casefold()
145
+ ):
146
+ continue
147
+ flt_flags[index] = 0
148
+
149
+ return (flt_flags, flt_neworder)
150
+
151
+ def draw_item(
152
+ self,
153
+ _context: Context,
154
+ layout: UILayout,
155
+ _data: object,
156
+ line: object,
157
+ _icon: int,
158
+ _active_data: object,
159
+ _active_prop_name: str,
160
+ _index: int,
161
+ _flt_flag: int,
162
+ ) -> None:
163
+ if not isinstance(line, VrmErrorDialogMessageLine):
164
+ return
165
+
166
+ column = layout.column(align=True)
167
+ column.label(text=line.line, translate=False)
168
+
169
+
170
+ class VRM_OT_save_error_dialog_message(Operator, ExportHelper):
171
+ bl_idname = "vrm.save_error_dialog_message"
172
+ bl_label = "Save Error Message"
173
+ bl_description = "Save Error Message"
174
+ bl_options: ClassVar = {"REGISTER"}
175
+
176
+ filename_ext = ".txt"
177
+ filter_glob: StringProperty( # type: ignore[valid-type]
178
+ default="*.txt",
179
+ options={"HIDDEN"},
180
+ )
181
+
182
+ title: StringProperty(options={"HIDDEN"}) # type: ignore[valid-type]
183
+ lines: CollectionProperty( # type: ignore[valid-type]
184
+ type=VrmErrorDialogMessageLine, options={"HIDDEN"}
185
+ )
186
+
187
+ def _restore_error_dialog(self) -> set[str]:
188
+ if bpy.app.version >= (4, 1):
189
+ return {"FINISHED"}
190
+
191
+ return show_error_dialog(
192
+ self.title,
193
+ [output.line for output in self.lines],
194
+ append_environment=False,
195
+ )
196
+
197
+ def cancel(self, _context: Context) -> None:
198
+ self._restore_error_dialog()
199
+
200
+ def execute(self, _context: Context) -> set[str]:
201
+ message = "\n".join(output.line for output in self.lines)
202
+ Path(self.filepath).write_bytes(message.encode())
203
+ return self._restore_error_dialog()
204
+
205
+ def invoke(self, context: Context, event: Event) -> set[str]:
206
+ if not self.filepath:
207
+ local_now = dt.datetime.now(tz=None) # Get local time # noqa: DTZ005
208
+ formatted_time = local_now.strftime("%Y%m%d_%H%M%S")
209
+ self.filepath = f"vrm_error_message_{formatted_time}.txt"
210
+ return ExportHelper.invoke(self, context, event)
211
+
212
+ if TYPE_CHECKING:
213
+ # This code is auto generated.
214
+ # To regenerate, run the `uv run tools/property_typing.py` command.
215
+ filter_glob: str # type: ignore[no-redef]
216
+ title: str # type: ignore[no-redef]
217
+ lines: CollectionPropertyProtocol[ # type: ignore[no-redef]
218
+ VrmErrorDialogMessageLine
219
+ ]
220
+
221
+
222
+ class WM_OT_vrm_error_dialog(Operator):
223
+ bl_label = "Error"
224
+ bl_idname = "wm.vrm_error_dialog"
225
+ bl_options: ClassVar = {"REGISTER"}
226
+
227
+ title: StringProperty(options={"HIDDEN"}) # type: ignore[valid-type]
228
+ lines: CollectionProperty( # type: ignore[valid-type]
229
+ type=VrmErrorDialogMessageLine, options={"HIDDEN"}
230
+ )
231
+ active_line_index: IntProperty(options={"HIDDEN"}) # type: ignore[valid-type]
232
+
233
+ def execute(self, _context: Context) -> set[str]:
234
+ return {"FINISHED"}
235
+
236
+ def invoke(self, context: Context, _event: Event) -> set[str]:
237
+ self.active_line_index = len(self.lines) - 1
238
+ return context.window_manager.invoke_props_dialog(self, width=700)
239
+
240
+ def draw(self, _context: Context) -> None:
241
+ layout = self.layout.column()
242
+ layout.emboss = "NONE"
243
+ box = layout.box()
244
+ box.emboss = "NORMAL"
245
+ row = box.split(factor=0.5, align=True)
246
+ row.label(text=self.title, icon="ERROR")
247
+ save_op = layout_operator(row, VRM_OT_save_error_dialog_message, icon="FILE")
248
+ save_op.title = self.title
249
+ for process_output_line in self.lines:
250
+ output = save_op.lines.add()
251
+ output.line = process_output_line.line
252
+ open_url_op = layout_operator(
253
+ row, VRM_OT_open_url_in_web_browser, icon="URL", text="Open Support Site"
254
+ )
255
+ open_url_op.url = "https://github.com/saturday06/VRM-Addon-for-Blender/issues"
256
+ box.template_list(
257
+ VRM_UL_vrm_error_dialog_message.bl_idname,
258
+ "",
259
+ self,
260
+ "lines",
261
+ self,
262
+ "active_line_index",
263
+ rows=20,
264
+ sort_lock=True,
265
+ )
266
+
267
+ if TYPE_CHECKING:
268
+ # This code is auto generated.
269
+ # To regenerate, run the `uv run tools/property_typing.py` command.
270
+ title: str # type: ignore[no-redef]
271
+ lines: CollectionPropertyProtocol[ # type: ignore[no-redef]
272
+ VrmErrorDialogMessageLine
273
+ ]
274
+ active_line_index: int # type: ignore[no-redef]
@@ -0,0 +1,71 @@
1
+ # SPDX-License-Identifier: MIT OR GPL-3.0-or-later
2
+ from pathlib import Path
3
+ from typing import Optional
4
+
5
+
6
+ def create_unique_indexed_directory_path(path: Path) -> Path:
7
+ name = path.name
8
+ max_retry_count = 100_000
9
+ for count in range(max_retry_count):
10
+ count_str = f".{count}" if count else ""
11
+ path = path.with_name(name + count_str)
12
+ try:
13
+ path.mkdir(parents=True)
14
+ except FileExistsError:
15
+ continue
16
+ except OSError as exception:
17
+ message = f"Failed to create unique directory path: {path}"
18
+ raise RuntimeError(message) from exception
19
+ return path
20
+ message = (
21
+ "Failed to create unique directory path "
22
+ + f"(max retries: {max_retry_count} exceeded): {path}"
23
+ )
24
+ raise RuntimeError(message)
25
+
26
+
27
+ def create_unique_indexed_file_path(path: Path, binary: Optional[bytes] = None) -> Path:
28
+ suffix = path.suffix
29
+ stem = path.stem
30
+ max_retry_count = 100_000
31
+
32
+ folder_path = path.parent
33
+ try:
34
+ folder_path.mkdir(parents=True, exist_ok=True)
35
+ except OSError as exception:
36
+ message = f"Failed to create parent directory for unique file path: {path}"
37
+ raise RuntimeError(message) from exception
38
+
39
+ for count in range(max_retry_count):
40
+ count_str = f".{count}" if count else ""
41
+ path = path.with_name(stem + count_str + suffix)
42
+
43
+ if binary is None:
44
+ try:
45
+ if not path.exists():
46
+ return path
47
+ except OSError as exception:
48
+ message = f"Failed to create unique file path: {path}"
49
+ raise RuntimeError(message) from exception
50
+ continue
51
+
52
+ try:
53
+ path.touch(exist_ok=False)
54
+ except FileExistsError:
55
+ continue
56
+ except OSError as exception:
57
+ message = f"Failed to create unique file path: {path}"
58
+ raise RuntimeError(message) from exception
59
+
60
+ try:
61
+ path.write_bytes(binary)
62
+ except OSError as exception:
63
+ message = f"Failed to write data to unique file path: {path}"
64
+ raise RuntimeError(message) from exception
65
+ return path
66
+
67
+ message = (
68
+ "Failed to create unique file path "
69
+ + f"(max retries: {max_retry_count} exceeded): {path}"
70
+ )
71
+ raise RuntimeError(message)
@@ -0,0 +1,20 @@
1
+ # SPDX-License-Identifier: MIT OR GPL-3.0-or-later
2
+ from typing import Final
3
+
4
+ GL_TRIANGLES: Final = 0x0004
5
+ GL_BYTE: Final = 0x1400
6
+ GL_UNSIGNED_BYTE: Final = 0x1401
7
+ GL_SHORT: Final = 0x1402
8
+ GL_UNSIGNED_SHORT: Final = 0x1403
9
+ GL_INT: Final = 0x1404
10
+ GL_UNSIGNED_INT: Final = 0x1405
11
+ GL_FLOAT: Final = 0x1406
12
+ GL_NEAREST: Final = 0x2600
13
+ GL_LINEAR: Final = 0x2601
14
+ GL_REPEAT: Final = 0x2901
15
+ GL_CLAMP_TO_EDGE: Final = 0x812F
16
+ GL_MIRRORED_REPEAT: Final = 0x8370
17
+ GL_NEAREST_MIPMAP_NEAREST: Final = 0x2700
18
+ GL_LINEAR_MIPMAP_NEAREST: Final = 0x2701
19
+ GL_NEAREST_MIPMAP_LINEAR: Final = 0x2702
20
+ GL_LINEAR_MIPMAP_LINEAR: Final = 0x2703