bpy-vrm-format 4.7.4__py3-none-any.whl
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.
- bpy_vrm_format-4.7.4.dist-info/METADATA +118 -0
- bpy_vrm_format-4.7.4.dist-info/RECORD +154 -0
- bpy_vrm_format-4.7.4.dist-info/WHEEL +4 -0
- io_scene_vrm/README.txt +3 -0
- io_scene_vrm/__init__.py +41 -0
- io_scene_vrm/blender_manifest.toml +28 -0
- io_scene_vrm/common/__init__.py +1 -0
- io_scene_vrm/common/animation.py +58 -0
- io_scene_vrm/common/blender_manifest.py +82 -0
- io_scene_vrm/common/char.py +16 -0
- io_scene_vrm/common/convert.py +287 -0
- io_scene_vrm/common/convert_any.py +48 -0
- io_scene_vrm/common/debug.py +40 -0
- io_scene_vrm/common/deep.py +162 -0
- io_scene_vrm/common/error_dialog.py +274 -0
- io_scene_vrm/common/fs.py +71 -0
- io_scene_vrm/common/gl.py +20 -0
- io_scene_vrm/common/gltf.py +1121 -0
- io_scene_vrm/common/human_bone_mapper/__init__.py +1 -0
- io_scene_vrm/common/human_bone_mapper/biped_mapping.py +84 -0
- io_scene_vrm/common/human_bone_mapper/cats_blender_plugin_fix_model_mapping.py +63 -0
- io_scene_vrm/common/human_bone_mapper/human_bone_mapper.py +203 -0
- io_scene_vrm/common/human_bone_mapper/microsoft_rocketbox_mapping.py +69 -0
- io_scene_vrm/common/human_bone_mapper/mixamo_mapping.py +62 -0
- io_scene_vrm/common/human_bone_mapper/mmd_mapping.py +115 -0
- io_scene_vrm/common/human_bone_mapper/mpfb_mapping.py +120 -0
- io_scene_vrm/common/human_bone_mapper/ready_player_me_mapping.py +65 -0
- io_scene_vrm/common/human_bone_mapper/reallusion_mapping.py +94 -0
- io_scene_vrm/common/human_bone_mapper/rigify_meta_rig_mapping.py +65 -0
- io_scene_vrm/common/human_bone_mapper/structure_based_mapping.py +891 -0
- io_scene_vrm/common/human_bone_mapper/unreal_mapping.py +62 -0
- io_scene_vrm/common/human_bone_mapper/vrm_addon_mapping.py +112 -0
- io_scene_vrm/common/human_bone_mapper/vroid_mapping.py +89 -0
- io_scene_vrm/common/legacy_gltf.py +16 -0
- io_scene_vrm/common/logger.py +84 -0
- io_scene_vrm/common/mtoon0.blend +0 -0
- io_scene_vrm/common/mtoon1.blend +0 -0
- io_scene_vrm/common/mtoon1_outline.blend +0 -0
- io_scene_vrm/common/mtoon_auto_setup.blend +0 -0
- io_scene_vrm/common/mtoon_unversioned.py +62 -0
- io_scene_vrm/common/ops/__init__.py +10 -0
- io_scene_vrm/common/ops/export_scene.py +67 -0
- io_scene_vrm/common/ops/icyp.py +53 -0
- io_scene_vrm/common/ops/import_scene.py +57 -0
- io_scene_vrm/common/ops/vrm.py +2380 -0
- io_scene_vrm/common/ops/wm.py +167 -0
- io_scene_vrm/common/preferences.py +362 -0
- io_scene_vrm/common/progress.py +87 -0
- io_scene_vrm/common/rotation.py +89 -0
- io_scene_vrm/common/safe_removal.py +46 -0
- io_scene_vrm/common/scene_watcher.py +230 -0
- io_scene_vrm/common/shader.py +2243 -0
- io_scene_vrm/common/shape_key_mapper/__init__.py +1 -0
- io_scene_vrm/common/shape_key_mapper/arkit_mapping.py +168 -0
- io_scene_vrm/common/shape_key_mapper/mmd_mapping.py +62 -0
- io_scene_vrm/common/shape_key_mapper/ready_player_me_mapping.py +52 -0
- io_scene_vrm/common/shape_key_mapper/vrchat_mapping.py +17 -0
- io_scene_vrm/common/third_party_user_extension.py +214 -0
- io_scene_vrm/common/transparent_z_write.blend +0 -0
- io_scene_vrm/common/version.py +161 -0
- io_scene_vrm/common/vrm0/__init__.py +1 -0
- io_scene_vrm/common/vrm0/human_bone.py +662 -0
- io_scene_vrm/common/vrm0/material_property.py +346 -0
- io_scene_vrm/common/vrm1/__init__.py +1 -0
- io_scene_vrm/common/vrm1/expression_preset.py +207 -0
- io_scene_vrm/common/vrm1/human_bone.py +822 -0
- io_scene_vrm/common/workspace.py +227 -0
- io_scene_vrm/common/writable_context.py +77 -0
- io_scene_vrm/editor/__init__.py +1 -0
- io_scene_vrm/editor/extension.py +425 -0
- io_scene_vrm/editor/extension_accessor.py +75 -0
- io_scene_vrm/editor/handler.py +84 -0
- io_scene_vrm/editor/khr_character/__init__.py +1 -0
- io_scene_vrm/editor/khr_character/panel.py +69 -0
- io_scene_vrm/editor/khr_character/property_group.py +27 -0
- io_scene_vrm/editor/khr_xmp_json_ld/__init__.py +1 -0
- io_scene_vrm/editor/khr_xmp_json_ld/ops.py +464 -0
- io_scene_vrm/editor/khr_xmp_json_ld/panel.py +136 -0
- io_scene_vrm/editor/khr_xmp_json_ld/property_group.py +83 -0
- io_scene_vrm/editor/khr_xmp_json_ld/ui_list.py +198 -0
- io_scene_vrm/editor/make_armature.py +709 -0
- io_scene_vrm/editor/menu.py +147 -0
- io_scene_vrm/editor/migration.py +297 -0
- io_scene_vrm/editor/mtoon1/__init__.py +1 -0
- io_scene_vrm/editor/mtoon1/handler.py +20 -0
- io_scene_vrm/editor/mtoon1/migration.py +536 -0
- io_scene_vrm/editor/mtoon1/ops.py +304 -0
- io_scene_vrm/editor/mtoon1/panel.py +442 -0
- io_scene_vrm/editor/mtoon1/property_group.py +4977 -0
- io_scene_vrm/editor/mtoon1/scene_watcher.py +381 -0
- io_scene_vrm/editor/node_constraint1/__init__.py +1 -0
- io_scene_vrm/editor/node_constraint1/panel.py +317 -0
- io_scene_vrm/editor/node_constraint1/property_group.py +25 -0
- io_scene_vrm/editor/ops.py +471 -0
- io_scene_vrm/editor/panel.py +315 -0
- io_scene_vrm/editor/property_group.py +888 -0
- io_scene_vrm/editor/search.py +682 -0
- io_scene_vrm/editor/spring_bone1/__init__.py +1 -0
- io_scene_vrm/editor/spring_bone1/handler.py +862 -0
- io_scene_vrm/editor/spring_bone1/menu.py +246 -0
- io_scene_vrm/editor/spring_bone1/migration.py +86 -0
- io_scene_vrm/editor/spring_bone1/ops.py +1663 -0
- io_scene_vrm/editor/spring_bone1/panel.py +584 -0
- io_scene_vrm/editor/spring_bone1/property_group.py +1355 -0
- io_scene_vrm/editor/spring_bone1/ui_list.py +408 -0
- io_scene_vrm/editor/subscription.py +123 -0
- io_scene_vrm/editor/t_pose.py +713 -0
- io_scene_vrm/editor/validation.py +1271 -0
- io_scene_vrm/editor/vrm0/__init__.py +1 -0
- io_scene_vrm/editor/vrm0/gizmo_group.py +63 -0
- io_scene_vrm/editor/vrm0/handler.py +23 -0
- io_scene_vrm/editor/vrm0/menu.py +145 -0
- io_scene_vrm/editor/vrm0/migration.py +761 -0
- io_scene_vrm/editor/vrm0/ops.py +2145 -0
- io_scene_vrm/editor/vrm0/panel.py +1333 -0
- io_scene_vrm/editor/vrm0/property_group.py +1818 -0
- io_scene_vrm/editor/vrm0/ui_list.py +688 -0
- io_scene_vrm/editor/vrm1/__init__.py +1 -0
- io_scene_vrm/editor/vrm1/handler.py +28 -0
- io_scene_vrm/editor/vrm1/menu.py +76 -0
- io_scene_vrm/editor/vrm1/migration.py +321 -0
- io_scene_vrm/editor/vrm1/ops.py +2679 -0
- io_scene_vrm/editor/vrm1/panel.py +1243 -0
- io_scene_vrm/editor/vrm1/property_group.py +2087 -0
- io_scene_vrm/editor/vrm1/scene_watcher.py +90 -0
- io_scene_vrm/editor/vrm1/ui_list.py +533 -0
- io_scene_vrm/exporter/__init__.py +1 -0
- io_scene_vrm/exporter/abstract_base_vrm_exporter.py +628 -0
- io_scene_vrm/exporter/export_scene.py +930 -0
- io_scene_vrm/exporter/gltf2_export_user_extension.py +45 -0
- io_scene_vrm/exporter/khr_character_exporter.py +34 -0
- io_scene_vrm/exporter/vrm0_exporter.py +4030 -0
- io_scene_vrm/exporter/vrm1_exporter.py +3653 -0
- io_scene_vrm/exporter/vrm_animation_exporter.py +1035 -0
- io_scene_vrm/external/__init__.py +1 -0
- io_scene_vrm/external/auto_rig_pro_support.py +3 -0
- io_scene_vrm/external/io_scene_gltf2_support.py +418 -0
- io_scene_vrm/external/rigify_support.py +1 -0
- io_scene_vrm/importer/__init__.py +1 -0
- io_scene_vrm/importer/abstract_base_vrm_importer.py +1494 -0
- io_scene_vrm/importer/file_handler.py +34 -0
- io_scene_vrm/importer/gltf2_import_user_extension.py +268 -0
- io_scene_vrm/importer/import_scene.py +646 -0
- io_scene_vrm/importer/license_validation.py +198 -0
- io_scene_vrm/importer/vrm0_importer.py +1730 -0
- io_scene_vrm/importer/vrm1_importer.py +1924 -0
- io_scene_vrm/importer/vrm_animation_importer.py +763 -0
- io_scene_vrm/importer/vrm_diff.py +316 -0
- io_scene_vrm/locale/__init__.py +1 -0
- io_scene_vrm/locale/ja_jp.py +826 -0
- io_scene_vrm/locale/ruff.toml +8 -0
- io_scene_vrm/locale/translation_dictionary.py +10 -0
- io_scene_vrm/locale/zh_hans.py +928 -0
- io_scene_vrm/registration.py +669 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT OR GPL-3.0-or-later
|
|
2
|
+
import math
|
|
3
|
+
from collections.abc import Iterator, Mapping, Sequence
|
|
4
|
+
from sys import float_info
|
|
5
|
+
from typing import Final, Optional, Union
|
|
6
|
+
|
|
7
|
+
from . import convert_any
|
|
8
|
+
from .logger import get_logger
|
|
9
|
+
|
|
10
|
+
_logger = get_logger(__name__)
|
|
11
|
+
|
|
12
|
+
BPY_TRACK_AXIS_TO_VRM_AIM_AXIS: Final[Mapping[str, str]] = {
|
|
13
|
+
"TRACK_X": "PositiveX",
|
|
14
|
+
"TRACK_Y": "PositiveY",
|
|
15
|
+
"TRACK_Z": "PositiveZ",
|
|
16
|
+
"TRACK_NEGATIVE_X": "NegativeX",
|
|
17
|
+
"TRACK_NEGATIVE_Y": "NegativeY",
|
|
18
|
+
"TRACK_NEGATIVE_Z": "NegativeZ",
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
VRM_AIM_AXIS_TO_BPY_TRACK_AXIS: Final[Mapping[str, str]] = {
|
|
22
|
+
v: k for k, v in BPY_TRACK_AXIS_TO_VRM_AIM_AXIS.items()
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
Json = Union[
|
|
27
|
+
None,
|
|
28
|
+
bool,
|
|
29
|
+
int,
|
|
30
|
+
float,
|
|
31
|
+
str,
|
|
32
|
+
list["Json"],
|
|
33
|
+
dict[str, "Json"],
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
JsonView = Union[
|
|
37
|
+
None,
|
|
38
|
+
bool,
|
|
39
|
+
int,
|
|
40
|
+
float,
|
|
41
|
+
str,
|
|
42
|
+
Sequence["JsonView"],
|
|
43
|
+
Mapping[str, "JsonView"],
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def iterator_or_none(v: object) -> Optional[Iterator[object]]:
|
|
48
|
+
try:
|
|
49
|
+
# "isinstance(v, Iterable)" doesn't work.
|
|
50
|
+
# https://github.com/python/cpython/blob/3.9/Doc/library/collections.abc.rst?plain=1#L126-L127
|
|
51
|
+
iterator = iter(v) # type: ignore[call-overload]
|
|
52
|
+
except TypeError:
|
|
53
|
+
return None
|
|
54
|
+
return convert_any.iterator_to_object_iterator(iterator)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def sequence_or_none(
|
|
58
|
+
sequence_object: object,
|
|
59
|
+
) -> Optional[Sequence[object]]:
|
|
60
|
+
iterator = iterator_or_none(sequence_object)
|
|
61
|
+
if iterator is None:
|
|
62
|
+
return None
|
|
63
|
+
return list(iterator)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def mapping_or_none(
|
|
67
|
+
mapping_object: object,
|
|
68
|
+
) -> Optional[Mapping[object, object]]:
|
|
69
|
+
return convert_any.mapping_to_object_mapping(mapping_object)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def vrm_json_vector3_to_tuple(
|
|
73
|
+
value: Json,
|
|
74
|
+
) -> Optional[tuple[float, float, float]]:
|
|
75
|
+
if not isinstance(value, dict):
|
|
76
|
+
return None
|
|
77
|
+
x = float_or(value.get("x"), 0.0)
|
|
78
|
+
y = float_or(value.get("y"), 0.0)
|
|
79
|
+
z = float_or(value.get("z"), 0.0)
|
|
80
|
+
return (x, y, z)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def vrm_json_curve_to_list(curve: Json) -> Optional[list[float]]:
|
|
84
|
+
if not isinstance(curve, list):
|
|
85
|
+
return None
|
|
86
|
+
values: list[float] = [float_or(v, 0.0) for v in curve]
|
|
87
|
+
while len(values) < 8:
|
|
88
|
+
values.append(0)
|
|
89
|
+
while len(values) > 8:
|
|
90
|
+
values.pop()
|
|
91
|
+
return values
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def vrm_json_array_to_float_vector(
|
|
95
|
+
input_values: Json, defaults: list[float]
|
|
96
|
+
) -> list[float]:
|
|
97
|
+
if not isinstance(input_values, list):
|
|
98
|
+
return defaults
|
|
99
|
+
|
|
100
|
+
output_values: list[float] = []
|
|
101
|
+
for index, default in enumerate(defaults):
|
|
102
|
+
if index >= len(input_values):
|
|
103
|
+
output_values.append(default)
|
|
104
|
+
continue
|
|
105
|
+
|
|
106
|
+
input_value = float_or(input_values[index], default)
|
|
107
|
+
output_values.append(input_value)
|
|
108
|
+
|
|
109
|
+
return output_values
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def mtoon_shading_toony_1_to_0(shading_toony: float, shading_shift: float) -> float:
|
|
113
|
+
base = 2 - shading_toony + shading_shift
|
|
114
|
+
if abs(base) < float_info.epsilon:
|
|
115
|
+
# https://github.com/Santarh/MToon/blob/43f02fe8c9b19c3cf9f3238003b7d8b332933833/MToon/Resources/Shaders/MToon.shader#L17
|
|
116
|
+
return 0.9
|
|
117
|
+
return max(0, min((shading_toony + shading_shift) / base, 1))
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def mtoon_shading_shift_1_to_0(shading_toony: float, shading_shift: float) -> float:
|
|
121
|
+
return max(-1, min(shading_toony - shading_shift - 1, 1))
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def mtoon_gi_equalization_to_intensity(gi_equalization: float) -> float:
|
|
125
|
+
return max(0, min(1.0, 1.0 - gi_equalization))
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
# https://github.com/vrm-c/UniVRM/blob/f3479190c330ec6ecd2b40be919285aa93a53aff/Assets/VRMShaders/VRM10/MToon10/Runtime/MToon10Migrator.cs#L10-L19
|
|
129
|
+
def mtoon_shading_toony_0_to_1(
|
|
130
|
+
shading_toony_0x: float, shading_shift_0x: float
|
|
131
|
+
) -> float:
|
|
132
|
+
(range_min, range_max) = _get_shading_range_0x(shading_toony_0x, shading_shift_0x)
|
|
133
|
+
return max(0, min(1.0, (2.0 - (range_max - range_min)) * 0.5))
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
# https://github.com/vrm-c/UniVRM/blob/f3479190c330ec6ecd2b40be919285aa93a53aff/Assets/VRMShaders/VRM10/MToon10/Runtime/MToon10Migrator.cs#L21-L30
|
|
137
|
+
def mtoon_shading_shift_0_to_1(
|
|
138
|
+
shading_toony_0x: float, shading_shift_0x: float
|
|
139
|
+
) -> float:
|
|
140
|
+
(range_min, range_max) = _get_shading_range_0x(shading_toony_0x, shading_shift_0x)
|
|
141
|
+
return max(-1, min(1.0, ((range_max + range_min) * 0.5 * -1)))
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
# https://github.com/vrm-c/UniVRM/blob/f3479190c330ec6ecd2b40be919285aa93a53aff/Assets/VRMShaders/VRM10/MToon10/Runtime/MToon10Migrator.cs#L32-L38
|
|
145
|
+
def mtoon_intensity_to_gi_equalization(gi_intensity_0x: float) -> float:
|
|
146
|
+
return max(0, min(1.0, 1.0 - gi_intensity_0x))
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
# https://github.com/vrm-c/UniVRM/blob/f3479190c330ec6ecd2b40be919285aa93a53aff/Assets/VRMShaders/VRM10/MToon10/Runtime/MToon10Migrator.cs#L40-L46
|
|
150
|
+
def _get_shading_range_0x(
|
|
151
|
+
shading_toony_0x: float, shading_shift_0x: float
|
|
152
|
+
) -> tuple[float, float]:
|
|
153
|
+
range_min = shading_shift_0x
|
|
154
|
+
range_max = (1 - shading_toony_0x) + shading_toony_0x * shading_shift_0x
|
|
155
|
+
return (range_min, range_max)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def float_or_none(
|
|
159
|
+
v: object,
|
|
160
|
+
min_value: float = -float_info.max,
|
|
161
|
+
max_value: float = float_info.max,
|
|
162
|
+
) -> Optional[float]:
|
|
163
|
+
if isinstance(v, float):
|
|
164
|
+
if math.isnan(v):
|
|
165
|
+
return None
|
|
166
|
+
return max(min_value, min(v, max_value))
|
|
167
|
+
|
|
168
|
+
if isinstance(v, bool):
|
|
169
|
+
return max(min_value, min(1.0 if v else 0.0, max_value))
|
|
170
|
+
|
|
171
|
+
if isinstance(v, int):
|
|
172
|
+
return max(min_value, min(float(v), max_value))
|
|
173
|
+
|
|
174
|
+
return None
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def float_or(
|
|
178
|
+
v: object,
|
|
179
|
+
default: float,
|
|
180
|
+
min_value: float = -float_info.max,
|
|
181
|
+
max_value: float = float_info.max,
|
|
182
|
+
) -> float:
|
|
183
|
+
float_or_none_value = float_or_none(v, min_value, max_value)
|
|
184
|
+
if float_or_none_value is not None:
|
|
185
|
+
return float_or_none_value
|
|
186
|
+
|
|
187
|
+
if min_value <= default <= max_value:
|
|
188
|
+
return default
|
|
189
|
+
|
|
190
|
+
_logger.warning(
|
|
191
|
+
"Default float value %s is out of range [%s, %s]", default, min_value, max_value
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
return max(min_value, min(default, max_value))
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def float4_or_none(value4: object) -> Optional[tuple[float, float, float, float]]:
|
|
198
|
+
value4_iterator = iterator_or_none(value4)
|
|
199
|
+
if value4_iterator is None:
|
|
200
|
+
return None
|
|
201
|
+
|
|
202
|
+
result: list[float] = []
|
|
203
|
+
for value in value4_iterator:
|
|
204
|
+
if len(result) == 4:
|
|
205
|
+
return None
|
|
206
|
+
float_value = float_or_none(value)
|
|
207
|
+
if float_value is None:
|
|
208
|
+
return None
|
|
209
|
+
result.append(float_value)
|
|
210
|
+
if len(result) != 4:
|
|
211
|
+
return None
|
|
212
|
+
return (result[0], result[1], result[2], result[3])
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def float4_or(
|
|
216
|
+
value4: object, default: tuple[float, float, float, float]
|
|
217
|
+
) -> tuple[float, float, float, float]:
|
|
218
|
+
float4 = float4_or_none(value4)
|
|
219
|
+
return float4 if float4 is not None else default
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def float3_or_none(value3: object) -> Optional[tuple[float, float, float]]:
|
|
223
|
+
value3_iterator = iterator_or_none(value3)
|
|
224
|
+
if value3_iterator is None:
|
|
225
|
+
return None
|
|
226
|
+
|
|
227
|
+
result: list[float] = []
|
|
228
|
+
for value in value3_iterator:
|
|
229
|
+
if len(result) == 3:
|
|
230
|
+
return None
|
|
231
|
+
float_value = float_or_none(value)
|
|
232
|
+
if float_value is None:
|
|
233
|
+
return None
|
|
234
|
+
result.append(float_value)
|
|
235
|
+
if len(result) != 3:
|
|
236
|
+
return None
|
|
237
|
+
return (result[0], result[1], result[2])
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def float3_or(
|
|
241
|
+
value3: object, default: tuple[float, float, float]
|
|
242
|
+
) -> tuple[float, float, float]:
|
|
243
|
+
float3 = float3_or_none(value3)
|
|
244
|
+
return float3 if float3 is not None else default
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def float2_or_none(value2: object) -> Optional[tuple[float, float]]:
|
|
248
|
+
value2_iterator = iterator_or_none(value2)
|
|
249
|
+
if value2_iterator is None:
|
|
250
|
+
return None
|
|
251
|
+
result: list[float] = []
|
|
252
|
+
for value in value2_iterator:
|
|
253
|
+
if len(result) == 2:
|
|
254
|
+
return None
|
|
255
|
+
float_value = float_or_none(value)
|
|
256
|
+
if float_value is None:
|
|
257
|
+
return None
|
|
258
|
+
result.append(float_value)
|
|
259
|
+
if len(result) != 2:
|
|
260
|
+
return None
|
|
261
|
+
return (result[0], result[1])
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def axis_blender_to_gltf(vector3: Sequence[float]) -> tuple[float, float, float]:
|
|
265
|
+
return (
|
|
266
|
+
-vector3[0],
|
|
267
|
+
vector3[2],
|
|
268
|
+
vector3[1],
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def linear_to_srgb(
|
|
273
|
+
non_color: Sequence[float],
|
|
274
|
+
) -> Sequence[float]:
|
|
275
|
+
return [
|
|
276
|
+
math.pow(channel_value, 1.0 / 2.2) if channel_index < 3 else channel_value
|
|
277
|
+
for channel_index, channel_value in enumerate(non_color)
|
|
278
|
+
]
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def srgb_to_linear(
|
|
282
|
+
srgb_color: Sequence[float],
|
|
283
|
+
) -> Sequence[float]:
|
|
284
|
+
return [
|
|
285
|
+
math.pow(channel_value, 2.2) if channel_index < 3 else channel_value
|
|
286
|
+
for channel_index, channel_value in enumerate(srgb_color)
|
|
287
|
+
]
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT OR GPL-3.0-or-later
|
|
2
|
+
"""A module provides a function to convert a variable of type Any to a concrete type.
|
|
3
|
+
|
|
4
|
+
Inevitably, variables of type Any may occur, and such variables cannot be handled
|
|
5
|
+
in type checkers in strict mode. Any is allowed only in the module here.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
from collections.abc import Iterator, Mapping
|
|
10
|
+
from typing import (
|
|
11
|
+
Any, # Any is allowed only in the module here.
|
|
12
|
+
Optional,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def to_object( # type: ignore[explicit-any]
|
|
17
|
+
any_object: Any, # noqa: ANN401 # Any is allowed only in the module here.
|
|
18
|
+
) -> object:
|
|
19
|
+
# Interpret Unknown as object
|
|
20
|
+
# https://github.com/microsoft/pyright/issues/3650
|
|
21
|
+
if not isinstance(any_object, object):
|
|
22
|
+
if sys.version_info >= (3, 11):
|
|
23
|
+
from typing import assert_never
|
|
24
|
+
|
|
25
|
+
assert_never(any_object)
|
|
26
|
+
raise TypeError
|
|
27
|
+
return any_object
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def iterator_to_object_iterator( # type: ignore[explicit-any]
|
|
31
|
+
any_iterator: Any, # noqa: ANN401 # Any is allowed only in the module here.
|
|
32
|
+
) -> Optional[Iterator[object]]:
|
|
33
|
+
any_iterator_without_partial_type_narrowing = any_iterator
|
|
34
|
+
if not isinstance(any_iterator, Iterator):
|
|
35
|
+
return None
|
|
36
|
+
return map(to_object, any_iterator_without_partial_type_narrowing)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def mapping_to_object_mapping( # type: ignore[explicit-any]
|
|
40
|
+
any_mapping: Any, # noqa: ANN401 # Any is allowed only in the module here.
|
|
41
|
+
) -> Optional[Mapping[object, object]]:
|
|
42
|
+
any_mapping_without_partial_type_narrowing = any_mapping
|
|
43
|
+
if not isinstance(any_mapping, Mapping):
|
|
44
|
+
return None
|
|
45
|
+
return {
|
|
46
|
+
to_object(k): to_object(v)
|
|
47
|
+
for k, v in any_mapping_without_partial_type_narrowing.items()
|
|
48
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT OR GPL-3.0-or-later
|
|
2
|
+
import math
|
|
3
|
+
from collections.abc import Sequence
|
|
4
|
+
from typing import Union
|
|
5
|
+
|
|
6
|
+
from mathutils import Matrix, Quaternion, Vector
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def dump(v: Union[Matrix, Vector, Quaternion, float]) -> str:
|
|
10
|
+
if isinstance(v, (float, int)):
|
|
11
|
+
return str(v)
|
|
12
|
+
|
|
13
|
+
if isinstance(v, Matrix):
|
|
14
|
+
t, r, s = v.decompose()
|
|
15
|
+
return f"Matrix(T={dump(t)},R={dump(r)},S={dump(s)})"
|
|
16
|
+
|
|
17
|
+
if isinstance(v, Vector):
|
|
18
|
+
return f"({v.x:.3f},{v.y:.3f},{v.z:.3f})"
|
|
19
|
+
|
|
20
|
+
x, y, z = (round(math.degrees(xyz)) for xyz in v.to_euler()[:])
|
|
21
|
+
return f"Euler({x},{y},{z})"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def assert_vector3_equals(
|
|
25
|
+
expected: Vector, actual: Sequence[float], message: str
|
|
26
|
+
) -> None:
|
|
27
|
+
if len(actual) != 3:
|
|
28
|
+
message = f"actual length is not 3: {actual}"
|
|
29
|
+
raise AssertionError(message)
|
|
30
|
+
|
|
31
|
+
threshold = 0.0001
|
|
32
|
+
if abs(expected[0] - actual[0]) > threshold:
|
|
33
|
+
message = f"{message}: {tuple(expected)} is different from {tuple(actual)}"
|
|
34
|
+
raise AssertionError(message)
|
|
35
|
+
if abs(expected[1] - actual[1]) > threshold:
|
|
36
|
+
message = f"{message}: {tuple(expected)} is different from {tuple(actual)}"
|
|
37
|
+
raise AssertionError(message)
|
|
38
|
+
if abs(expected[2] - actual[2]) > threshold:
|
|
39
|
+
message = f"{message}: {tuple(expected)} is different from {tuple(actual)}"
|
|
40
|
+
raise AssertionError(message)
|
|
@@ -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)
|