@volter/blender-engine 0.1.148 → 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.
- package/LICENSE +16 -0
- package/README.md +4 -11
- package/browser/addons/vrm/LICENSE_(OPTION1)_MIT.txt +22 -0
- package/browser/addons/vrm/LICENSE_(OPTION2)_GPL-3_0.txt +674 -0
- package/browser/addons/vrm/LICENSE_MAIN.txt +1 -0
- package/browser/addons/vrm/README.txt +3 -0
- package/browser/addons/vrm/__init__.py +41 -0
- package/browser/addons/vrm/blender_manifest.toml +16 -0
- package/browser/addons/vrm/common/__init__.py +1 -0
- package/browser/addons/vrm/common/animation.py +58 -0
- package/browser/addons/vrm/common/blender_manifest.py +82 -0
- package/browser/addons/vrm/common/char.py +16 -0
- package/browser/addons/vrm/common/convert.py +287 -0
- package/browser/addons/vrm/common/convert_any.py +48 -0
- package/browser/addons/vrm/common/debug.py +40 -0
- package/browser/addons/vrm/common/deep.py +162 -0
- package/browser/addons/vrm/common/error_dialog.py +274 -0
- package/browser/addons/vrm/common/fs.py +71 -0
- package/browser/addons/vrm/common/gl.py +20 -0
- package/browser/addons/vrm/common/gltf.py +1149 -0
- package/browser/addons/vrm/common/human_bone_mapper/__init__.py +1 -0
- package/browser/addons/vrm/common/human_bone_mapper/biped_mapping.py +84 -0
- package/browser/addons/vrm/common/human_bone_mapper/cats_blender_plugin_fix_model_mapping.py +63 -0
- package/browser/addons/vrm/common/human_bone_mapper/human_bone_mapper.py +203 -0
- package/browser/addons/vrm/common/human_bone_mapper/microsoft_rocketbox_mapping.py +69 -0
- package/browser/addons/vrm/common/human_bone_mapper/mixamo_mapping.py +62 -0
- package/browser/addons/vrm/common/human_bone_mapper/mmd_mapping.py +115 -0
- package/browser/addons/vrm/common/human_bone_mapper/mpfb_mapping.py +120 -0
- package/browser/addons/vrm/common/human_bone_mapper/ready_player_me_mapping.py +65 -0
- package/browser/addons/vrm/common/human_bone_mapper/reallusion_mapping.py +94 -0
- package/browser/addons/vrm/common/human_bone_mapper/rigify_meta_rig_mapping.py +65 -0
- package/browser/addons/vrm/common/human_bone_mapper/structure_based_mapping.py +891 -0
- package/browser/addons/vrm/common/human_bone_mapper/unreal_mapping.py +62 -0
- package/browser/addons/vrm/common/human_bone_mapper/vrm_addon_mapping.py +112 -0
- package/browser/addons/vrm/common/human_bone_mapper/vroid_mapping.py +89 -0
- package/browser/addons/vrm/common/legacy_gltf.py +16 -0
- package/browser/addons/vrm/common/logger.py +84 -0
- package/browser/addons/vrm/common/mtoon0.blend +0 -0
- package/browser/addons/vrm/common/mtoon1.blend +0 -0
- package/browser/addons/vrm/common/mtoon1_outline.blend +0 -0
- package/browser/addons/vrm/common/mtoon_auto_setup.blend +0 -0
- package/browser/addons/vrm/common/mtoon_unversioned.py +62 -0
- package/browser/addons/vrm/common/ops/__init__.py +10 -0
- package/browser/addons/vrm/common/ops/export_scene.py +67 -0
- package/browser/addons/vrm/common/ops/icyp.py +53 -0
- package/browser/addons/vrm/common/ops/import_scene.py +57 -0
- package/browser/addons/vrm/common/ops/vrm.py +2380 -0
- package/browser/addons/vrm/common/ops/wm.py +167 -0
- package/browser/addons/vrm/common/preferences.py +362 -0
- package/browser/addons/vrm/common/progress.py +87 -0
- package/browser/addons/vrm/common/rotation.py +89 -0
- package/browser/addons/vrm/common/safe_removal.py +46 -0
- package/browser/addons/vrm/common/scene_watcher.py +230 -0
- package/browser/addons/vrm/common/shader.py +2243 -0
- package/browser/addons/vrm/common/shape_key_mapper/__init__.py +1 -0
- package/browser/addons/vrm/common/shape_key_mapper/arkit_mapping.py +168 -0
- package/browser/addons/vrm/common/shape_key_mapper/mmd_mapping.py +62 -0
- package/browser/addons/vrm/common/shape_key_mapper/ready_player_me_mapping.py +52 -0
- package/browser/addons/vrm/common/shape_key_mapper/vrchat_mapping.py +17 -0
- package/browser/addons/vrm/common/third_party_user_extension.py +214 -0
- package/browser/addons/vrm/common/transparent_z_write.blend +0 -0
- package/browser/addons/vrm/common/version.py +161 -0
- package/browser/addons/vrm/common/vrm0/__init__.py +1 -0
- package/browser/addons/vrm/common/vrm0/human_bone.py +662 -0
- package/browser/addons/vrm/common/vrm0/material_property.py +346 -0
- package/browser/addons/vrm/common/vrm1/__init__.py +1 -0
- package/browser/addons/vrm/common/vrm1/expression_preset.py +207 -0
- package/browser/addons/vrm/common/vrm1/human_bone.py +822 -0
- package/browser/addons/vrm/common/workspace.py +227 -0
- package/browser/addons/vrm/common/writable_context.py +77 -0
- package/browser/addons/vrm/documentation/en-us/animation/index.md +63 -0
- package/browser/addons/vrm/documentation/en-us/create-humanoid-vrm-from-scratch/index.md +131 -0
- package/browser/addons/vrm/documentation/en-us/create-simple-vrm-from-scratch/index.md +30 -0
- package/browser/addons/vrm/documentation/en-us/development/index.md +136 -0
- package/browser/addons/vrm/documentation/en-us/index.md +41 -0
- package/browser/addons/vrm/documentation/en-us/installation/index.md +152 -0
- package/browser/addons/vrm/documentation/en-us/material-mtoon/index.md +122 -0
- package/browser/addons/vrm/documentation/en-us/material-pbr/index.md +94 -0
- package/browser/addons/vrm/documentation/en-us/scripting-api/index.md +391 -0
- package/browser/addons/vrm/documentation/en-us/ui/export_scene.vrm/index.md +57 -0
- package/browser/addons/vrm/documentation/en-us/ui/export_scene.vrma/index.md +17 -0
- package/browser/addons/vrm/documentation/en-us/ui/import_scene.vrm/index.md +61 -0
- package/browser/addons/vrm/documentation/en-us/ui/import_scene.vrma/index.md +13 -0
- package/browser/addons/vrm/documentation/ja-jp/animation/index.md +57 -0
- package/browser/addons/vrm/documentation/ja-jp/create-humanoid-vrm-from-scratch/index.md +129 -0
- package/browser/addons/vrm/documentation/ja-jp/create-simple-vrm-from-scratch/index.md +29 -0
- package/browser/addons/vrm/documentation/ja-jp/development/index.md +137 -0
- package/browser/addons/vrm/documentation/ja-jp/index.md +37 -0
- package/browser/addons/vrm/documentation/ja-jp/installation/index.md +150 -0
- package/browser/addons/vrm/documentation/ja-jp/material-mtoon/index.md +113 -0
- package/browser/addons/vrm/documentation/ja-jp/material-pbr/index.md +90 -0
- package/browser/addons/vrm/documentation/ja-jp/scripting-api/index.md +389 -0
- package/browser/addons/vrm/documentation/ja-jp/ui/export_scene.vrm/index.md +55 -0
- package/browser/addons/vrm/documentation/ja-jp/ui/export_scene.vrma/index.md +19 -0
- package/browser/addons/vrm/documentation/ja-jp/ui/import_scene.vrm/index.md +56 -0
- package/browser/addons/vrm/documentation/ja-jp/ui/import_scene.vrma/index.md +14 -0
- package/browser/addons/vrm/editor/__init__.py +1 -0
- package/browser/addons/vrm/editor/extension.py +425 -0
- package/browser/addons/vrm/editor/extension_accessor.py +75 -0
- package/browser/addons/vrm/editor/handler.py +84 -0
- package/browser/addons/vrm/editor/khr_character/__init__.py +1 -0
- package/browser/addons/vrm/editor/khr_character/panel.py +69 -0
- package/browser/addons/vrm/editor/khr_character/property_group.py +27 -0
- package/browser/addons/vrm/editor/khr_xmp_json_ld/__init__.py +1 -0
- package/browser/addons/vrm/editor/khr_xmp_json_ld/ops.py +464 -0
- package/browser/addons/vrm/editor/khr_xmp_json_ld/panel.py +136 -0
- package/browser/addons/vrm/editor/khr_xmp_json_ld/property_group.py +83 -0
- package/browser/addons/vrm/editor/khr_xmp_json_ld/ui_list.py +198 -0
- package/browser/addons/vrm/editor/make_armature.py +709 -0
- package/browser/addons/vrm/editor/menu.py +147 -0
- package/browser/addons/vrm/editor/migration.py +297 -0
- package/browser/addons/vrm/editor/mtoon1/__init__.py +1 -0
- package/browser/addons/vrm/editor/mtoon1/handler.py +20 -0
- package/browser/addons/vrm/editor/mtoon1/migration.py +536 -0
- package/browser/addons/vrm/editor/mtoon1/ops.py +304 -0
- package/browser/addons/vrm/editor/mtoon1/panel.py +442 -0
- package/browser/addons/vrm/editor/mtoon1/property_group.py +4977 -0
- package/browser/addons/vrm/editor/mtoon1/scene_watcher.py +381 -0
- package/browser/addons/vrm/editor/node_constraint1/__init__.py +1 -0
- package/browser/addons/vrm/editor/node_constraint1/panel.py +317 -0
- package/browser/addons/vrm/editor/node_constraint1/property_group.py +25 -0
- package/browser/addons/vrm/editor/ops.py +471 -0
- package/browser/addons/vrm/editor/panel.py +315 -0
- package/browser/addons/vrm/editor/property_group.py +888 -0
- package/browser/addons/vrm/editor/search.py +682 -0
- package/browser/addons/vrm/editor/spring_bone1/__init__.py +1 -0
- package/browser/addons/vrm/editor/spring_bone1/handler.py +994 -0
- package/browser/addons/vrm/editor/spring_bone1/menu.py +246 -0
- package/browser/addons/vrm/editor/spring_bone1/migration.py +86 -0
- package/browser/addons/vrm/editor/spring_bone1/ops.py +1679 -0
- package/browser/addons/vrm/editor/spring_bone1/panel.py +605 -0
- package/browser/addons/vrm/editor/spring_bone1/property_group.py +1457 -0
- package/browser/addons/vrm/editor/spring_bone1/ui_list.py +408 -0
- package/browser/addons/vrm/editor/subscription.py +123 -0
- package/browser/addons/vrm/editor/t_pose.py +713 -0
- package/browser/addons/vrm/editor/validation.py +1271 -0
- package/browser/addons/vrm/editor/vrm0/__init__.py +1 -0
- package/browser/addons/vrm/editor/vrm0/gizmo_group.py +63 -0
- package/browser/addons/vrm/editor/vrm0/handler.py +23 -0
- package/browser/addons/vrm/editor/vrm0/menu.py +145 -0
- package/browser/addons/vrm/editor/vrm0/migration.py +761 -0
- package/browser/addons/vrm/editor/vrm0/ops.py +2145 -0
- package/browser/addons/vrm/editor/vrm0/panel.py +1333 -0
- package/browser/addons/vrm/editor/vrm0/property_group.py +1818 -0
- package/browser/addons/vrm/editor/vrm0/ui_list.py +688 -0
- package/browser/addons/vrm/editor/vrm1/__init__.py +1 -0
- package/browser/addons/vrm/editor/vrm1/handler.py +28 -0
- package/browser/addons/vrm/editor/vrm1/menu.py +76 -0
- package/browser/addons/vrm/editor/vrm1/migration.py +321 -0
- package/browser/addons/vrm/editor/vrm1/ops.py +2679 -0
- package/browser/addons/vrm/editor/vrm1/panel.py +1243 -0
- package/browser/addons/vrm/editor/vrm1/property_group.py +2087 -0
- package/browser/addons/vrm/editor/vrm1/scene_watcher.py +90 -0
- package/browser/addons/vrm/editor/vrm1/ui_list.py +533 -0
- package/browser/addons/vrm/exporter/__init__.py +1 -0
- package/browser/addons/vrm/exporter/abstract_base_vrm_exporter.py +628 -0
- package/browser/addons/vrm/exporter/export_scene.py +930 -0
- package/browser/addons/vrm/exporter/gltf2_export_user_extension.py +45 -0
- package/browser/addons/vrm/exporter/khr_character_exporter.py +34 -0
- package/browser/addons/vrm/exporter/vrm0_exporter.py +4020 -0
- package/browser/addons/vrm/exporter/vrm1_exporter.py +3682 -0
- package/browser/addons/vrm/exporter/vrm_animation_exporter.py +1035 -0
- package/browser/addons/vrm/external/__init__.py +1 -0
- package/browser/addons/vrm/external/auto_rig_pro_support.py +3 -0
- package/browser/addons/vrm/external/io_scene_gltf2_support.py +418 -0
- package/browser/addons/vrm/external/rigify_support.py +1 -0
- package/browser/addons/vrm/importer/__init__.py +1 -0
- package/browser/addons/vrm/importer/abstract_base_vrm_importer.py +1548 -0
- package/browser/addons/vrm/importer/file_handler.py +34 -0
- package/browser/addons/vrm/importer/gltf2_import_user_extension.py +268 -0
- package/browser/addons/vrm/importer/import_scene.py +646 -0
- package/browser/addons/vrm/importer/license_validation.py +198 -0
- package/browser/addons/vrm/importer/vrm0_importer.py +1723 -0
- package/browser/addons/vrm/importer/vrm1_importer.py +2014 -0
- package/browser/addons/vrm/importer/vrm_animation_importer.py +763 -0
- package/browser/addons/vrm/importer/vrm_diff.py +316 -0
- package/browser/addons/vrm/locale/__init__.py +1 -0
- package/browser/addons/vrm/locale/ja_jp.py +826 -0
- package/browser/addons/vrm/locale/ruff.toml +8 -0
- package/browser/addons/vrm/locale/translation_dictionary.py +10 -0
- package/browser/addons/vrm/locale/zh_hans.py +928 -0
- package/browser/addons/vrm/registration.py +670 -0
- package/browser/addons/vrm.json +18 -0
- package/browser/blender-emscripten-engine.mts +60 -128
- package/browser/blender-engine.mts +20 -186
- package/browser/blender-wali-engine.mts +35 -81
- package/browser/describe-thrown.mts +3 -6
- package/browser/document-chunks.mts +4 -16
- package/browser/gltf_export.py +3161 -0
- package/browser/heap-growth.mts +23 -0
- package/browser/index.ts +2 -0
- package/browser/protocol.ts +119 -35
- package/browser/pull-job.mts +2 -10
- package/browser/rna.ts +190 -73
- package/browser/runtime.ts +197 -82
- package/browser/session-addons.mts +33 -0
- package/browser/session-frame.mts +5 -24
- package/browser/session.py +1338 -1427
- package/browser/three/attach-presenter.ts +11 -39
- package/browser/three/blender-base64.ts +1 -36
- package/browser/three/blender-corner-normals.ts +1 -23
- package/browser/three/blender-display-shaders.json +1 -1
- package/browser/three/blender-display-shaders.lut +0 -0
- package/browser/three/blender-display-transform.ts +2 -1
- package/browser/three/blender-frame-columns.ts +5 -13
- package/browser/three/blender-graph-material.ts +18 -3
- package/browser/three/blender-node-graph.ts +10 -34
- package/browser/three/blender-physical-material.ts +2 -31
- package/browser/three/blender-runtime-armature.ts +7 -60
- package/browser/three/blender-runtime-frame.ts +9 -23
- package/browser/three/blender-runtime-geometry.ts +22 -26
- package/browser/three/blender-runtime-lighting.ts +48 -178
- package/browser/three/blender-runtime-shadows.ts +187 -1
- package/browser/three/blender-runtime-skeleton.ts +53 -1
- package/browser/three/blender-runtime-view.ts +505 -283
- package/browser/three/blender-runtime-weights.ts +9 -72
- package/browser/three/blender-sky.ts +13 -33
- package/browser/three/blender-triangulate.ts +7 -35
- package/browser/three/blender-viewport-sampling.ts +235 -0
- package/browser/three/blender-workbench-material.ts +25 -29
- package/browser/three/blender-workbench-outline.ts +199 -0
- package/browser/three/blender-world-volume.ts +62 -10
- package/browser/three/presenter.ts +10 -26
- package/browser/three/release.ts +5 -21
- package/browser/three/sky-precompute-worker.ts +1 -18
- package/browser/three/sky-worker.ts +5 -23
- package/browser/three/world-field-sampler.ts +12 -26
- package/browser/volter_three.py +9 -105
- package/browser/worker.ts +156 -151
- package/package.json +5 -2
- package/wasm/BUNDLE.json +17 -17
- package/wasm/DEPENDENCY-LICENSES.txt +14 -0
- package/wasm/blender_browser.data.br +0 -0
- package/wasm/blender_browser.js +1 -1
- package/wasm/blender_browser.wasm.br +0 -0
- package/browser/frame-transfer.mts +0 -19
- package/browser/three/agx-base-srgb.lut +0 -0
- package/browser/three/agx-look-medium-high-contrast.lut +0 -0
- package/browser/three/agx-look-punchy.lut +0 -0
- package/browser/three/blender-agx.ts +0 -235
- package/browser/three/blender-display-lut.ts +0 -145
- package/browser/three/blender-filmic.ts +0 -49
- package/browser/three/blender-standard.ts +0 -68
- package/browser/three/blender-world-visibility.ts +0 -278
- package/browser/three/filmic-srgb.lut +0 -0
|
@@ -0,0 +1,888 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT OR GPL-3.0-or-later
|
|
2
|
+
import uuid
|
|
3
|
+
from collections.abc import Iterable, Iterator, Mapping, Sequence, ValuesView
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from enum import Enum
|
|
6
|
+
from typing import (
|
|
7
|
+
TYPE_CHECKING,
|
|
8
|
+
ClassVar,
|
|
9
|
+
Final,
|
|
10
|
+
Optional,
|
|
11
|
+
Protocol,
|
|
12
|
+
TypeVar,
|
|
13
|
+
Union,
|
|
14
|
+
overload,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
import bpy
|
|
18
|
+
from bpy.app.translations import pgettext
|
|
19
|
+
from bpy.props import FloatProperty, PointerProperty, StringProperty
|
|
20
|
+
from bpy.types import Armature, Bone, Context, Material, Object, PropertyGroup, UILayout
|
|
21
|
+
from mathutils import Vector
|
|
22
|
+
|
|
23
|
+
from ..common import shader
|
|
24
|
+
from ..common.logger import get_logger
|
|
25
|
+
from ..common.vrm0 import human_bone as vrm0_human_bone
|
|
26
|
+
from ..common.vrm1 import human_bone as vrm1_human_bone
|
|
27
|
+
from .extension_accessor import (
|
|
28
|
+
get_armature_extension,
|
|
29
|
+
get_bone_extension,
|
|
30
|
+
get_material_extension,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
HumanBoneSpecification = TypeVar(
|
|
34
|
+
"HumanBoneSpecification",
|
|
35
|
+
vrm0_human_bone.HumanBoneSpecification,
|
|
36
|
+
vrm1_human_bone.HumanBoneSpecification,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
_logger = get_logger(__name__)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# https://docs.blender.org/api/2.93/bpy.types.EnumPropertyItem.html#bpy.types.EnumPropertyItem
|
|
43
|
+
@dataclass(frozen=True)
|
|
44
|
+
class PropertyGroupEnumItem:
|
|
45
|
+
identifier: str
|
|
46
|
+
name: str
|
|
47
|
+
description: str
|
|
48
|
+
icon: str
|
|
49
|
+
value: int
|
|
50
|
+
|
|
51
|
+
@staticmethod
|
|
52
|
+
def from_enum_property_items(
|
|
53
|
+
items: tuple[tuple[str, str, str, str, int], ...],
|
|
54
|
+
) -> tuple[tuple[str, ...], tuple[int, ...], tuple["PropertyGroupEnumItem", ...]]:
|
|
55
|
+
enum_items = tuple(
|
|
56
|
+
PropertyGroupEnumItem(
|
|
57
|
+
identifier=identifier,
|
|
58
|
+
name=name,
|
|
59
|
+
description=description,
|
|
60
|
+
icon=icon,
|
|
61
|
+
value=value,
|
|
62
|
+
)
|
|
63
|
+
for identifier, name, description, icon, value in items
|
|
64
|
+
)
|
|
65
|
+
identifiers = tuple(enum_item.identifier for enum_item in enum_items)
|
|
66
|
+
values = tuple(enum_item.value for enum_item in enum_items)
|
|
67
|
+
return identifiers, values, enum_items
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class PropertyGroupEnum:
|
|
71
|
+
def __init__(self, enum_items: tuple[tuple[str, str, str, str, int], ...]) -> None:
|
|
72
|
+
self.enum_items = enum_items
|
|
73
|
+
|
|
74
|
+
def items(self) -> tuple[tuple[str, str, str, str, int], ...]:
|
|
75
|
+
return self.enum_items
|
|
76
|
+
|
|
77
|
+
def value_to_identifier(self, value: int, default: str) -> str:
|
|
78
|
+
return next(
|
|
79
|
+
(enum.identifier for enum in self if enum.value == value),
|
|
80
|
+
default,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
def identifier_to_value(self, identifier: str, default: int) -> int:
|
|
84
|
+
return next(
|
|
85
|
+
(enum.value for enum in self if enum.identifier == identifier),
|
|
86
|
+
default,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
def identifiers(self) -> tuple[str, ...]:
|
|
90
|
+
return tuple(enum.identifier for enum in self)
|
|
91
|
+
|
|
92
|
+
def values(self) -> tuple[int, ...]:
|
|
93
|
+
return tuple(enum.value for enum in self)
|
|
94
|
+
|
|
95
|
+
def __iter__(self) -> Iterator[PropertyGroupEnumItem]:
|
|
96
|
+
return (
|
|
97
|
+
PropertyGroupEnumItem(
|
|
98
|
+
identifier=identifier,
|
|
99
|
+
name=name,
|
|
100
|
+
description=description,
|
|
101
|
+
icon=icon,
|
|
102
|
+
value=value,
|
|
103
|
+
)
|
|
104
|
+
for identifier, name, description, icon, value in self.items()
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def property_group_enum(
|
|
109
|
+
*items: tuple[str, str, str, str, int],
|
|
110
|
+
) -> tuple[PropertyGroupEnum, Iterator[PropertyGroupEnumItem]]:
|
|
111
|
+
enum = PropertyGroupEnum(items)
|
|
112
|
+
return enum, enum.__iter__()
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class StringPropertyGroup(PropertyGroup):
|
|
116
|
+
def _get_value(self) -> str:
|
|
117
|
+
value = self.get("value")
|
|
118
|
+
if isinstance(value, str):
|
|
119
|
+
return value
|
|
120
|
+
return str(value)
|
|
121
|
+
|
|
122
|
+
def _set_value(self, value: str) -> None:
|
|
123
|
+
self.name = value
|
|
124
|
+
self["value"] = value
|
|
125
|
+
|
|
126
|
+
value: StringProperty( # type: ignore[valid-type]
|
|
127
|
+
name="String Value",
|
|
128
|
+
get=_get_value,
|
|
129
|
+
set=_set_value,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
if TYPE_CHECKING:
|
|
133
|
+
# This code is auto generated.
|
|
134
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
135
|
+
value: str # type: ignore[no-redef]
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class FloatPropertyGroup(PropertyGroup):
|
|
139
|
+
def _get_value(self) -> float:
|
|
140
|
+
value = self.get("value")
|
|
141
|
+
if isinstance(value, (float, int)):
|
|
142
|
+
return float(value)
|
|
143
|
+
return 0.0
|
|
144
|
+
|
|
145
|
+
def _set_value(self, value: float) -> None:
|
|
146
|
+
self.name = str(value)
|
|
147
|
+
self["value"] = value
|
|
148
|
+
|
|
149
|
+
value: FloatProperty( # type: ignore[valid-type]
|
|
150
|
+
name="Float Value",
|
|
151
|
+
get=_get_value,
|
|
152
|
+
set=_set_value,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
if TYPE_CHECKING:
|
|
156
|
+
# This code is auto generated.
|
|
157
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
158
|
+
value: float # type: ignore[no-redef]
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class MeshObjectPropertyGroup(PropertyGroup):
|
|
162
|
+
def _get_mesh_object_name(self) -> str:
|
|
163
|
+
bpy_object = self.bpy_object
|
|
164
|
+
if not bpy_object or not bpy_object.name or bpy_object.type != "MESH":
|
|
165
|
+
return ""
|
|
166
|
+
return str(bpy_object.name)
|
|
167
|
+
|
|
168
|
+
def _set_mesh_object_name(self, value: object) -> None:
|
|
169
|
+
context = bpy.context
|
|
170
|
+
|
|
171
|
+
if (
|
|
172
|
+
not isinstance(value, str)
|
|
173
|
+
or not (obj := context.blend_data.objects.get(value))
|
|
174
|
+
or obj.type != "MESH"
|
|
175
|
+
):
|
|
176
|
+
if self.bpy_object:
|
|
177
|
+
self.bpy_object = None
|
|
178
|
+
return
|
|
179
|
+
|
|
180
|
+
if self.bpy_object != obj:
|
|
181
|
+
self.bpy_object = obj
|
|
182
|
+
|
|
183
|
+
mesh_object_name: StringProperty( # type: ignore[valid-type]
|
|
184
|
+
get=_get_mesh_object_name, set=_set_mesh_object_name
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
saved_mesh_object_name_to_restore: StringProperty() # type: ignore[valid-type]
|
|
188
|
+
|
|
189
|
+
def _poll_bpy_object(self, obj: object) -> bool:
|
|
190
|
+
return isinstance(obj, Object) and obj.type == "MESH"
|
|
191
|
+
|
|
192
|
+
def _update_bpy_object(self, _context: Context) -> None:
|
|
193
|
+
bpy_object = self.bpy_object
|
|
194
|
+
self.saved_mesh_object_name_to_restore = bpy_object.name if bpy_object else ""
|
|
195
|
+
|
|
196
|
+
bpy_object: PointerProperty( # type: ignore[valid-type]
|
|
197
|
+
type=Object,
|
|
198
|
+
poll=_poll_bpy_object,
|
|
199
|
+
update=_update_bpy_object,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
def restore_object_assignment(self, context: Context) -> None:
|
|
203
|
+
if self.bpy_object:
|
|
204
|
+
return
|
|
205
|
+
obj = context.blend_data.objects.get(self.saved_mesh_object_name_to_restore)
|
|
206
|
+
if not obj:
|
|
207
|
+
return
|
|
208
|
+
self._set_mesh_object_name(obj.name)
|
|
209
|
+
|
|
210
|
+
if TYPE_CHECKING:
|
|
211
|
+
# This code is auto generated.
|
|
212
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
213
|
+
mesh_object_name: str # type: ignore[no-redef]
|
|
214
|
+
saved_mesh_object_name_to_restore: str # type: ignore[no-redef]
|
|
215
|
+
bpy_object: Optional[Object] # type: ignore[no-redef]
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
class MaterialPropertyGroup(PropertyGroup):
|
|
219
|
+
material: PointerProperty( # type: ignore[valid-type]
|
|
220
|
+
type=Material,
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
if TYPE_CHECKING:
|
|
224
|
+
# This code is auto generated.
|
|
225
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
226
|
+
material: Optional[Material] # type: ignore[no-redef]
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
class BonePropertyGroupType(Enum):
|
|
230
|
+
VRM0_FIRST_PERSON = 1
|
|
231
|
+
VRM0_HUMAN = 2
|
|
232
|
+
VRM0_COLLIDER_GROUP = 3
|
|
233
|
+
VRM0_BONE_GROUP_CENTER = 4
|
|
234
|
+
VRM0_BONE_GROUP = 5
|
|
235
|
+
VRM1_HUMAN = 6
|
|
236
|
+
SPRING_BONE1_COLLIDER = 7
|
|
237
|
+
SPRING_BONE1_SPRING_CENTER = 8
|
|
238
|
+
SPRING_BONE1_SPRING_JOINT = 9
|
|
239
|
+
|
|
240
|
+
@staticmethod
|
|
241
|
+
def is_vrm0(bone_property_group_type: "BonePropertyGroupType") -> bool:
|
|
242
|
+
return bone_property_group_type in {
|
|
243
|
+
BonePropertyGroupType.VRM0_FIRST_PERSON,
|
|
244
|
+
BonePropertyGroupType.VRM0_HUMAN,
|
|
245
|
+
BonePropertyGroupType.VRM0_COLLIDER_GROUP,
|
|
246
|
+
BonePropertyGroupType.VRM0_BONE_GROUP_CENTER,
|
|
247
|
+
BonePropertyGroupType.VRM0_BONE_GROUP,
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
@staticmethod
|
|
251
|
+
def is_vrm1(bone_property_group_type: "BonePropertyGroupType") -> bool:
|
|
252
|
+
return bone_property_group_type in {
|
|
253
|
+
BonePropertyGroupType.VRM1_HUMAN,
|
|
254
|
+
BonePropertyGroupType.SPRING_BONE1_COLLIDER,
|
|
255
|
+
BonePropertyGroupType.SPRING_BONE1_SPRING_CENTER,
|
|
256
|
+
BonePropertyGroupType.SPRING_BONE1_SPRING_JOINT,
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
class BonePropertyGroup(PropertyGroup):
|
|
261
|
+
@staticmethod
|
|
262
|
+
def get_all_bone_property_groups(
|
|
263
|
+
armature: Union[Object, Armature],
|
|
264
|
+
) -> Iterator[tuple["BonePropertyGroup", BonePropertyGroupType]]:
|
|
265
|
+
"""Return (bone: BonePropertyGroup, is_vrm0: bool, is_vrm1: bool)."""
|
|
266
|
+
if isinstance(armature, Object):
|
|
267
|
+
armature_data = armature.data
|
|
268
|
+
if not isinstance(armature_data, Armature):
|
|
269
|
+
return
|
|
270
|
+
else:
|
|
271
|
+
armature_data = armature
|
|
272
|
+
|
|
273
|
+
ext = get_armature_extension(armature_data)
|
|
274
|
+
yield (
|
|
275
|
+
ext.vrm0.first_person.first_person_bone,
|
|
276
|
+
BonePropertyGroupType.VRM0_FIRST_PERSON,
|
|
277
|
+
)
|
|
278
|
+
for human_bone in ext.vrm0.humanoid.human_bones:
|
|
279
|
+
yield human_bone.node, BonePropertyGroupType.VRM0_HUMAN
|
|
280
|
+
for collider_group in ext.vrm0.secondary_animation.collider_groups:
|
|
281
|
+
yield collider_group.node, BonePropertyGroupType.VRM0_COLLIDER_GROUP
|
|
282
|
+
for bone_group in ext.vrm0.secondary_animation.bone_groups:
|
|
283
|
+
yield bone_group.center, BonePropertyGroupType.VRM0_BONE_GROUP_CENTER
|
|
284
|
+
yield from (
|
|
285
|
+
(bone, BonePropertyGroupType.VRM0_BONE_GROUP)
|
|
286
|
+
for bone in bone_group.bones
|
|
287
|
+
)
|
|
288
|
+
for (
|
|
289
|
+
human_bone
|
|
290
|
+
) in ext.vrm1.humanoid.human_bones.human_bone_name_to_human_bone().values():
|
|
291
|
+
yield human_bone.node, BonePropertyGroupType.VRM1_HUMAN
|
|
292
|
+
for collider in ext.spring_bone1.colliders:
|
|
293
|
+
yield collider.node, BonePropertyGroupType.SPRING_BONE1_COLLIDER
|
|
294
|
+
for spring in ext.spring_bone1.springs:
|
|
295
|
+
yield spring.center, BonePropertyGroupType.SPRING_BONE1_SPRING_CENTER
|
|
296
|
+
for joint in spring.joints:
|
|
297
|
+
yield joint.node, BonePropertyGroupType.SPRING_BONE1_SPRING_JOINT
|
|
298
|
+
|
|
299
|
+
armature_data_name_and_bone_uuid_to_bone_name_cache: ClassVar[
|
|
300
|
+
dict[tuple[str, str], str]
|
|
301
|
+
] = {}
|
|
302
|
+
|
|
303
|
+
def _get_bone_name(self) -> str:
|
|
304
|
+
if not self.bone_uuid:
|
|
305
|
+
return ""
|
|
306
|
+
|
|
307
|
+
armature_data = self.find_armature()
|
|
308
|
+
|
|
309
|
+
# Use cache to speed up this function since profiling showed it was slow
|
|
310
|
+
cache_key = (armature_data.name, self.bone_uuid)
|
|
311
|
+
cached_bone_name = self.armature_data_name_and_bone_uuid_to_bone_name_cache.get(
|
|
312
|
+
cache_key
|
|
313
|
+
)
|
|
314
|
+
if cached_bone_name is not None:
|
|
315
|
+
if (
|
|
316
|
+
cached_bone := armature_data.bones.get(cached_bone_name)
|
|
317
|
+
) and get_bone_extension(cached_bone).uuid == self.bone_uuid:
|
|
318
|
+
return cached_bone_name
|
|
319
|
+
# If there's even one old value in the cache, clear everything for safety
|
|
320
|
+
self.armature_data_name_and_bone_uuid_to_bone_name_cache.clear()
|
|
321
|
+
|
|
322
|
+
for bone in armature_data.bones:
|
|
323
|
+
if get_bone_extension(bone).uuid == self.bone_uuid:
|
|
324
|
+
self.armature_data_name_and_bone_uuid_to_bone_name_cache[cache_key] = (
|
|
325
|
+
bone.name
|
|
326
|
+
)
|
|
327
|
+
return bone.name
|
|
328
|
+
|
|
329
|
+
return ""
|
|
330
|
+
|
|
331
|
+
def find_armature(self) -> Armature:
|
|
332
|
+
id_data = self.id_data
|
|
333
|
+
if isinstance(id_data, Armature):
|
|
334
|
+
return id_data
|
|
335
|
+
|
|
336
|
+
message = (
|
|
337
|
+
f"{type(self)}/{self}.id_data is not a {Armature}"
|
|
338
|
+
+ f" but {type(id_data)}/{id_data}"
|
|
339
|
+
)
|
|
340
|
+
raise AssertionError(message)
|
|
341
|
+
|
|
342
|
+
def get_bone_property_group_type(self) -> BonePropertyGroupType:
|
|
343
|
+
armature_data = self.find_armature()
|
|
344
|
+
for (
|
|
345
|
+
bone_property_group,
|
|
346
|
+
bone_property_group_type,
|
|
347
|
+
) in self.get_all_bone_property_groups(armature_data):
|
|
348
|
+
if bone_property_group == self:
|
|
349
|
+
return bone_property_group_type
|
|
350
|
+
|
|
351
|
+
message = (
|
|
352
|
+
f"{type(self)}/{self} is not associated with "
|
|
353
|
+
+ "BonePropertyGroupType.get_all_bone_property_groups"
|
|
354
|
+
)
|
|
355
|
+
raise AssertionError(message)
|
|
356
|
+
|
|
357
|
+
def _set_bone_name(self, value: str) -> None:
|
|
358
|
+
context = bpy.context
|
|
359
|
+
|
|
360
|
+
armature_data = self.find_armature()
|
|
361
|
+
armature_objects = [
|
|
362
|
+
obj for obj in context.blend_data.objects if obj.data == armature_data
|
|
363
|
+
]
|
|
364
|
+
bone_property_group_type = self.get_bone_property_group_type()
|
|
365
|
+
|
|
366
|
+
# Assign UUIDs and regenerate it if duplication exist
|
|
367
|
+
found_uuids: set[str] = set()
|
|
368
|
+
for bone in armature_data.bones:
|
|
369
|
+
bone_extension = get_bone_extension(bone)
|
|
370
|
+
found_uuid = bone_extension.uuid
|
|
371
|
+
if not found_uuid or found_uuid in found_uuids:
|
|
372
|
+
bone_extension.uuid = uuid.uuid4().hex
|
|
373
|
+
self.armature_data_name_and_bone_uuid_to_bone_name_cache.clear()
|
|
374
|
+
found_uuids.add(bone_extension.uuid)
|
|
375
|
+
|
|
376
|
+
if not value or not (bone := armature_data.bones.get(value)):
|
|
377
|
+
if not self.bone_uuid:
|
|
378
|
+
# Not changed
|
|
379
|
+
return
|
|
380
|
+
self.bone_uuid = ""
|
|
381
|
+
elif self.bone_uuid and self.bone_uuid == get_bone_extension(bone).uuid:
|
|
382
|
+
# Not changed
|
|
383
|
+
return
|
|
384
|
+
else:
|
|
385
|
+
self.bone_uuid = get_bone_extension(bone).uuid
|
|
386
|
+
|
|
387
|
+
ext = get_armature_extension(armature_data)
|
|
388
|
+
|
|
389
|
+
if bone_property_group_type in (
|
|
390
|
+
BonePropertyGroupType.VRM0_COLLIDER_GROUP,
|
|
391
|
+
BonePropertyGroupType.VRM0_BONE_GROUP,
|
|
392
|
+
):
|
|
393
|
+
for collider_group in ext.vrm0.secondary_animation.collider_groups:
|
|
394
|
+
for armature_object in armature_objects:
|
|
395
|
+
collider_group.fixup(armature_object)
|
|
396
|
+
|
|
397
|
+
if bone_property_group_type == BonePropertyGroupType.SPRING_BONE1_COLLIDER:
|
|
398
|
+
for collider in ext.spring_bone1.colliders:
|
|
399
|
+
for armature_object in armature_objects:
|
|
400
|
+
collider.reset_bpy_object(context, armature_object)
|
|
401
|
+
|
|
402
|
+
if bone_property_group_type == BonePropertyGroupType.VRM0_HUMAN:
|
|
403
|
+
HumanoidStructureBonePropertyGroup.update_all_vrm0_bone_name_candidates(
|
|
404
|
+
armature_data
|
|
405
|
+
)
|
|
406
|
+
if bone_property_group_type == BonePropertyGroupType.VRM1_HUMAN:
|
|
407
|
+
HumanoidStructureBonePropertyGroup.update_all_vrm1_bone_name_candidates(
|
|
408
|
+
armature_data
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
bone_name: StringProperty( # type: ignore[valid-type]
|
|
412
|
+
name="Bone",
|
|
413
|
+
get=_get_bone_name,
|
|
414
|
+
set=_set_bone_name,
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
def filter_bone_names(self, bone_names: Iterable[str]) -> Sequence[str]:
|
|
418
|
+
return list(bone_names)
|
|
419
|
+
|
|
420
|
+
bone_uuid: StringProperty() # type: ignore[valid-type]
|
|
421
|
+
|
|
422
|
+
if TYPE_CHECKING:
|
|
423
|
+
# This code is auto generated.
|
|
424
|
+
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
425
|
+
bone_name: str # type: ignore[no-redef]
|
|
426
|
+
bone_uuid: str # type: ignore[no-redef]
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
class HumanoidStructureBonePropertyGroup(BonePropertyGroup):
|
|
430
|
+
pointer_to_bone_name_candidates: ClassVar[dict[int, set[str]]] = {}
|
|
431
|
+
|
|
432
|
+
@classmethod
|
|
433
|
+
def clear_bone_name_candidates_cache(
|
|
434
|
+
cls, humanoid_structure_bones: Iterable["HumanoidStructureBonePropertyGroup"]
|
|
435
|
+
) -> None:
|
|
436
|
+
for humanoid_structure_bone in humanoid_structure_bones:
|
|
437
|
+
cls.pointer_to_bone_name_candidates.pop(
|
|
438
|
+
humanoid_structure_bone.as_pointer(), None
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
@property
|
|
442
|
+
def bone_name_candidates_or_none(self) -> Optional[set[str]]:
|
|
443
|
+
pointer_key = self.as_pointer()
|
|
444
|
+
return self.pointer_to_bone_name_candidates.get(pointer_key)
|
|
445
|
+
|
|
446
|
+
@property
|
|
447
|
+
def bone_name_candidates(self) -> set[str]:
|
|
448
|
+
pointer_key = self.as_pointer()
|
|
449
|
+
bone_name_candidates = self.pointer_to_bone_name_candidates.get(pointer_key)
|
|
450
|
+
if bone_name_candidates is None:
|
|
451
|
+
bone_name_candidates = set[str]()
|
|
452
|
+
self.pointer_to_bone_name_candidates[pointer_key] = bone_name_candidates
|
|
453
|
+
armature_data = self.find_armature()
|
|
454
|
+
bone_property_group_type = self.get_bone_property_group_type()
|
|
455
|
+
if bone_property_group_type == BonePropertyGroupType.VRM0_HUMAN:
|
|
456
|
+
HumanoidStructureBonePropertyGroup.update_all_vrm0_bone_name_candidates(
|
|
457
|
+
armature_data
|
|
458
|
+
)
|
|
459
|
+
elif bone_property_group_type == BonePropertyGroupType.VRM1_HUMAN:
|
|
460
|
+
HumanoidStructureBonePropertyGroup.update_all_vrm1_bone_name_candidates(
|
|
461
|
+
armature_data
|
|
462
|
+
)
|
|
463
|
+
return bone_name_candidates
|
|
464
|
+
|
|
465
|
+
def filter_bone_names(self, bone_names: Iterable[str]) -> Sequence[str]:
|
|
466
|
+
bone_name_candidates = self.bone_name_candidates
|
|
467
|
+
return [
|
|
468
|
+
bone_name for bone_name in bone_names if bone_name in bone_name_candidates
|
|
469
|
+
]
|
|
470
|
+
|
|
471
|
+
@staticmethod
|
|
472
|
+
def find_bone_candidates(
|
|
473
|
+
armature_data: Armature,
|
|
474
|
+
target: HumanBoneSpecification,
|
|
475
|
+
bpy_bone_name_to_human_bone_specification: Mapping[str, HumanBoneSpecification],
|
|
476
|
+
error_bpy_bone_names: Sequence[str],
|
|
477
|
+
diagnostics_layout: Optional[UILayout] = None,
|
|
478
|
+
) -> set[str]:
|
|
479
|
+
bones = armature_data.bones
|
|
480
|
+
human_bone_name_to_bpy_bone_name = {
|
|
481
|
+
value.name: key
|
|
482
|
+
for key, value in bpy_bone_name_to_human_bone_specification.items()
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
search_conditions: list[str] = []
|
|
486
|
+
|
|
487
|
+
# If the target's ancestor has a Human Bone assignment, use that as the
|
|
488
|
+
# search start bone
|
|
489
|
+
searching_bones: Optional[list[Bone]] = None
|
|
490
|
+
ancestor: Optional[HumanBoneSpecification] = target.parent
|
|
491
|
+
while ancestor:
|
|
492
|
+
ancestor_bpy_bone_name = human_bone_name_to_bpy_bone_name.get(ancestor.name)
|
|
493
|
+
if not ancestor_bpy_bone_name or not (
|
|
494
|
+
ancestor_bpy_bone := bones.get(ancestor_bpy_bone_name)
|
|
495
|
+
):
|
|
496
|
+
# If there's no Human Bone assignment, traverse to the parent
|
|
497
|
+
ancestor = ancestor.parent
|
|
498
|
+
continue
|
|
499
|
+
|
|
500
|
+
if ancestor_bpy_bone.name in error_bpy_bone_names:
|
|
501
|
+
# If there's an error in the Human Bone assignment, return empty result
|
|
502
|
+
if diagnostics_layout:
|
|
503
|
+
diagnostics_message = pgettext(
|
|
504
|
+
'The bone assigned to the VRM Human Bone "{human_bone}" must'
|
|
505
|
+
+ " be descendants of the bones assigned \nto the VRM human"
|
|
506
|
+
+ ' bone "{parent_human_bone}". However, it cannot retrieve'
|
|
507
|
+
+ " bone candidates because there\nis an error in the"
|
|
508
|
+
+ ' assignment of the VRM Human Bone "{parent_human_bone}".'
|
|
509
|
+
+ " Please resolve the error in the\nassignment of the VRM"
|
|
510
|
+
+ ' Human Bone "{parent_human_bone}" first.'
|
|
511
|
+
).format(
|
|
512
|
+
human_bone=target.title,
|
|
513
|
+
parent_human_bone=ancestor.title,
|
|
514
|
+
)
|
|
515
|
+
diagnostics_column = diagnostics_layout.column(align=True)
|
|
516
|
+
for i, line in enumerate(diagnostics_message.splitlines()):
|
|
517
|
+
diagnostics_column.label(
|
|
518
|
+
text=line,
|
|
519
|
+
translate=False,
|
|
520
|
+
icon="ERROR" if i == 0 else "BLANK1",
|
|
521
|
+
)
|
|
522
|
+
return set()
|
|
523
|
+
|
|
524
|
+
# Use the child bones of the found ancestor bone as the search start bones
|
|
525
|
+
searching_bones = list(ancestor_bpy_bone.children)
|
|
526
|
+
if diagnostics_layout:
|
|
527
|
+
search_conditions.append(
|
|
528
|
+
pgettext(
|
|
529
|
+
'Being a descendant of the bone "{bpy_bone}" assigned'
|
|
530
|
+
+ ' to the VRM Human Bone "{human_bone}"'
|
|
531
|
+
).format(
|
|
532
|
+
human_bone=ancestor.title,
|
|
533
|
+
bpy_bone=ancestor_bpy_bone_name,
|
|
534
|
+
)
|
|
535
|
+
)
|
|
536
|
+
break
|
|
537
|
+
|
|
538
|
+
root_bones: Final[Sequence[Bone]] = [
|
|
539
|
+
bone for bone in bones.values() if not bone.parent
|
|
540
|
+
]
|
|
541
|
+
|
|
542
|
+
# If no ancestor bone is found, use the root bone as the search start bone
|
|
543
|
+
if searching_bones is None and len(root_bones) > 1:
|
|
544
|
+
# If there are multiple root bones,
|
|
545
|
+
# select the root bone of the already assigned bone
|
|
546
|
+
human_bone_specification = None
|
|
547
|
+
for (
|
|
548
|
+
bpy_bone_name,
|
|
549
|
+
human_bone_specification,
|
|
550
|
+
) in bpy_bone_name_to_human_bone_specification.items():
|
|
551
|
+
if not bpy_bone_name:
|
|
552
|
+
continue
|
|
553
|
+
if bpy_bone_name in error_bpy_bone_names:
|
|
554
|
+
continue
|
|
555
|
+
bpy_bone = bones.get(bpy_bone_name)
|
|
556
|
+
if not bpy_bone:
|
|
557
|
+
continue
|
|
558
|
+
|
|
559
|
+
main_root_bone: Optional[Bone] = None
|
|
560
|
+
parent: Optional[Bone] = bpy_bone
|
|
561
|
+
while parent:
|
|
562
|
+
main_root_bone = parent
|
|
563
|
+
parent = parent.parent
|
|
564
|
+
if not main_root_bone:
|
|
565
|
+
continue
|
|
566
|
+
|
|
567
|
+
searching_bones = [main_root_bone]
|
|
568
|
+
if diagnostics_layout:
|
|
569
|
+
search_conditions.append(
|
|
570
|
+
pgettext(
|
|
571
|
+
'Sharing the root bone with the bone "{bpy_bone}" assigned'
|
|
572
|
+
+ ' to the VRM Human Bone "{human_bone}"'
|
|
573
|
+
).format(
|
|
574
|
+
human_bone=human_bone_specification.title,
|
|
575
|
+
bpy_bone=bpy_bone_name,
|
|
576
|
+
)
|
|
577
|
+
)
|
|
578
|
+
break
|
|
579
|
+
if searching_bones is None:
|
|
580
|
+
searching_bones = list(root_bones)
|
|
581
|
+
|
|
582
|
+
# Bone candidates
|
|
583
|
+
bone_candidates: set[Bone] = set(searching_bones)
|
|
584
|
+
|
|
585
|
+
# First, register all descendant bones as candidates
|
|
586
|
+
filling_bones = searching_bones.copy()
|
|
587
|
+
while filling_bones:
|
|
588
|
+
filling_bone = filling_bones.pop()
|
|
589
|
+
bone_candidates.update(filling_bone.children)
|
|
590
|
+
filling_bones.extend(filling_bone.children)
|
|
591
|
+
|
|
592
|
+
removing_bone_tree = set[Bone]()
|
|
593
|
+
|
|
594
|
+
# Traverse descendant bones and when we encounter an already assigned bone,
|
|
595
|
+
# examine its relationship and exclude unnecessary candidates.
|
|
596
|
+
while searching_bones:
|
|
597
|
+
searching_bone = searching_bones.pop()
|
|
598
|
+
human_bone_specification = bpy_bone_name_to_human_bone_specification.get(
|
|
599
|
+
searching_bone.name
|
|
600
|
+
)
|
|
601
|
+
|
|
602
|
+
# If not assigned to a Human Bone or if it's the target bone,
|
|
603
|
+
# recursively traverse child bones
|
|
604
|
+
if (
|
|
605
|
+
human_bone_specification is None
|
|
606
|
+
or human_bone_specification == target
|
|
607
|
+
or searching_bone.name in error_bpy_bone_names
|
|
608
|
+
):
|
|
609
|
+
searching_bones.extend(searching_bone.children)
|
|
610
|
+
continue
|
|
611
|
+
|
|
612
|
+
if human_bone_specification.is_ancestor_of(target):
|
|
613
|
+
# If an ancestor bone has an assignment
|
|
614
|
+
# This case shouldn't exist since we start from the nearest
|
|
615
|
+
# ancestor bone
|
|
616
|
+
continue
|
|
617
|
+
|
|
618
|
+
if target.is_ancestor_of(human_bone_specification):
|
|
619
|
+
# If a descendant bone has an assignment:
|
|
620
|
+
# - Exclude that bone and its descendants
|
|
621
|
+
# - Exclude branches when traversing from that bone to the root bone
|
|
622
|
+
removing_bone_tree.add(searching_bone)
|
|
623
|
+
|
|
624
|
+
parent = searching_bone
|
|
625
|
+
while parent:
|
|
626
|
+
grand_parent = parent.parent
|
|
627
|
+
if grand_parent is None:
|
|
628
|
+
# If parent is a root bone,
|
|
629
|
+
# exclude other root bones except parent
|
|
630
|
+
removing_bone_tree.update(
|
|
631
|
+
root_bone for root_bone in root_bones if root_bone != parent
|
|
632
|
+
)
|
|
633
|
+
break
|
|
634
|
+
# Exclude sibling bones of parent
|
|
635
|
+
removing_bone_tree.update(
|
|
636
|
+
parent_sibling
|
|
637
|
+
for parent_sibling in grand_parent.children
|
|
638
|
+
if parent_sibling != parent
|
|
639
|
+
)
|
|
640
|
+
parent = grand_parent
|
|
641
|
+
|
|
642
|
+
if diagnostics_layout:
|
|
643
|
+
search_conditions.append(
|
|
644
|
+
pgettext(
|
|
645
|
+
'Being an ancestor of the bone "{bpy_bone}" assigned'
|
|
646
|
+
+ ' to the VRM Human Bone "{human_bone}"'
|
|
647
|
+
).format(
|
|
648
|
+
human_bone=human_bone_specification.title,
|
|
649
|
+
bpy_bone=searching_bone.name,
|
|
650
|
+
)
|
|
651
|
+
)
|
|
652
|
+
else:
|
|
653
|
+
# If a bone that is neither ancestor nor descendant has an assignment:
|
|
654
|
+
# - Exclude that bone and its descendants
|
|
655
|
+
# - Exclude that bone and its ancestors
|
|
656
|
+
removing_bone_tree.add(searching_bone)
|
|
657
|
+
|
|
658
|
+
parent = searching_bone
|
|
659
|
+
while parent:
|
|
660
|
+
bone_candidates.discard(parent)
|
|
661
|
+
parent = parent.parent
|
|
662
|
+
if diagnostics_layout:
|
|
663
|
+
search_conditions.append(
|
|
664
|
+
pgettext(
|
|
665
|
+
'Not being an ancestor of the bone "{bpy_bone}" assigned'
|
|
666
|
+
+ ' to the VRM Human Bone "{human_bone}"'
|
|
667
|
+
).format(
|
|
668
|
+
human_bone=human_bone_specification.title,
|
|
669
|
+
bpy_bone=searching_bone.name,
|
|
670
|
+
)
|
|
671
|
+
)
|
|
672
|
+
|
|
673
|
+
bone_candidates.difference_update(removing_bone_tree)
|
|
674
|
+
while removing_bone_tree:
|
|
675
|
+
removing_bone = removing_bone_tree.pop()
|
|
676
|
+
bone_candidates.difference_update(removing_bone.children)
|
|
677
|
+
removing_bone_tree.update(removing_bone.children)
|
|
678
|
+
|
|
679
|
+
if diagnostics_layout and search_conditions:
|
|
680
|
+
diagnostics_layout.label(
|
|
681
|
+
text=pgettext(
|
|
682
|
+
"Bones that meet all of the following conditions will be "
|
|
683
|
+
+ "candidates for assignment:"
|
|
684
|
+
),
|
|
685
|
+
translate=False,
|
|
686
|
+
icon="INFO",
|
|
687
|
+
)
|
|
688
|
+
for search_condition in sorted(search_conditions):
|
|
689
|
+
diagnostics_layout.label(
|
|
690
|
+
text=search_condition,
|
|
691
|
+
translate=False,
|
|
692
|
+
icon="DOT",
|
|
693
|
+
)
|
|
694
|
+
|
|
695
|
+
return {bone_cancidate.name for bone_cancidate in bone_candidates}
|
|
696
|
+
|
|
697
|
+
@staticmethod
|
|
698
|
+
def update_all_vrm0_bone_name_candidates(armature_data: Armature) -> None:
|
|
699
|
+
ext = get_armature_extension(armature_data)
|
|
700
|
+
|
|
701
|
+
bpy_bone_name_to_human_bone_specification: dict[
|
|
702
|
+
str, vrm0_human_bone.HumanBoneSpecification
|
|
703
|
+
] = {}
|
|
704
|
+
for human_bone in ext.vrm0.humanoid.human_bones:
|
|
705
|
+
if not human_bone.node.bone_name:
|
|
706
|
+
continue
|
|
707
|
+
human_bone_name = vrm0_human_bone.HumanBoneName.from_str(human_bone.bone)
|
|
708
|
+
if human_bone_name is None:
|
|
709
|
+
continue
|
|
710
|
+
bpy_bone_name_to_human_bone_specification[human_bone.node.bone_name] = (
|
|
711
|
+
vrm0_human_bone.HumanBoneSpecifications.get(human_bone_name)
|
|
712
|
+
)
|
|
713
|
+
|
|
714
|
+
# Set from parent to child since child nodes will always have errors
|
|
715
|
+
# if parent node has errors
|
|
716
|
+
traversing_human_bone_specifications = [
|
|
717
|
+
vrm0_human_bone.HumanBoneSpecifications.HIPS
|
|
718
|
+
]
|
|
719
|
+
error_bpy_bone_names = []
|
|
720
|
+
bone_name_candidates_updated = True
|
|
721
|
+
while traversing_human_bone_specifications:
|
|
722
|
+
traversing_human_bone_specification = (
|
|
723
|
+
traversing_human_bone_specifications.pop()
|
|
724
|
+
)
|
|
725
|
+
|
|
726
|
+
if bone_name_candidates_updated:
|
|
727
|
+
error_bpy_bone_names = [
|
|
728
|
+
error_human_bone.node.bone_name
|
|
729
|
+
for error_human_bone in ext.vrm0.humanoid.human_bones
|
|
730
|
+
if (bone_name := error_human_bone.node.bone_name)
|
|
731
|
+
and (
|
|
732
|
+
bone_name_candidates
|
|
733
|
+
:= error_human_bone.node.bone_name_candidates_or_none
|
|
734
|
+
)
|
|
735
|
+
is not None
|
|
736
|
+
and bone_name not in bone_name_candidates
|
|
737
|
+
]
|
|
738
|
+
|
|
739
|
+
human_bone, human_bone_name = next(
|
|
740
|
+
(
|
|
741
|
+
(human_bone, human_bone_name)
|
|
742
|
+
for human_bone in ext.vrm0.humanoid.human_bones
|
|
743
|
+
if (
|
|
744
|
+
human_bone_name := vrm0_human_bone.HumanBoneName.from_str(
|
|
745
|
+
human_bone.bone
|
|
746
|
+
)
|
|
747
|
+
)
|
|
748
|
+
== human_bone_name
|
|
749
|
+
== traversing_human_bone_specification.name
|
|
750
|
+
),
|
|
751
|
+
(None, None),
|
|
752
|
+
)
|
|
753
|
+
if human_bone and human_bone_name:
|
|
754
|
+
bone_name_candidates_updated = (
|
|
755
|
+
human_bone.node.update_bone_name_candidates(
|
|
756
|
+
armature_data,
|
|
757
|
+
vrm0_human_bone.HumanBoneSpecifications.get(human_bone_name),
|
|
758
|
+
bpy_bone_name_to_human_bone_specification,
|
|
759
|
+
error_bpy_bone_names,
|
|
760
|
+
)
|
|
761
|
+
)
|
|
762
|
+
|
|
763
|
+
traversing_human_bone_specifications.extend(
|
|
764
|
+
traversing_human_bone_specification.children
|
|
765
|
+
)
|
|
766
|
+
|
|
767
|
+
@staticmethod
|
|
768
|
+
def update_all_vrm1_bone_name_candidates(armature_data: Armature) -> None:
|
|
769
|
+
ext = get_armature_extension(armature_data)
|
|
770
|
+
|
|
771
|
+
human_bone_name_to_human_bone = (
|
|
772
|
+
ext.vrm1.humanoid.human_bones.human_bone_name_to_human_bone()
|
|
773
|
+
)
|
|
774
|
+
bpy_bone_name_to_human_bone_specification: dict[
|
|
775
|
+
str, vrm1_human_bone.HumanBoneSpecification
|
|
776
|
+
] = {}
|
|
777
|
+
for human_bone_name, human_bone in human_bone_name_to_human_bone.items():
|
|
778
|
+
if not human_bone.node.bone_name:
|
|
779
|
+
continue
|
|
780
|
+
bpy_bone_name_to_human_bone_specification[human_bone.node.bone_name] = (
|
|
781
|
+
vrm1_human_bone.HumanBoneSpecifications.get(human_bone_name)
|
|
782
|
+
)
|
|
783
|
+
|
|
784
|
+
# Set from parent to child since child nodes will always have errors
|
|
785
|
+
# if parent node has errors
|
|
786
|
+
traversing_human_bone_specifications = [
|
|
787
|
+
vrm1_human_bone.HumanBoneSpecifications.HIPS
|
|
788
|
+
]
|
|
789
|
+
error_bpy_bone_names = []
|
|
790
|
+
bone_name_candidates_updated = True
|
|
791
|
+
while traversing_human_bone_specifications:
|
|
792
|
+
traversing_human_bone_specification = (
|
|
793
|
+
traversing_human_bone_specifications.pop()
|
|
794
|
+
)
|
|
795
|
+
|
|
796
|
+
if bone_name_candidates_updated:
|
|
797
|
+
error_bpy_bone_names = [
|
|
798
|
+
error_human_bone.node.bone_name
|
|
799
|
+
for error_human_bone in human_bone_name_to_human_bone.values()
|
|
800
|
+
if (bone_name := error_human_bone.node.bone_name)
|
|
801
|
+
and (
|
|
802
|
+
bone_name_candidates
|
|
803
|
+
:= error_human_bone.node.bone_name_candidates_or_none
|
|
804
|
+
)
|
|
805
|
+
is not None
|
|
806
|
+
and bone_name not in bone_name_candidates
|
|
807
|
+
]
|
|
808
|
+
|
|
809
|
+
human_bone = human_bone_name_to_human_bone[
|
|
810
|
+
traversing_human_bone_specification.name
|
|
811
|
+
]
|
|
812
|
+
bone_name_candidates_updated = human_bone.node.update_bone_name_candidates(
|
|
813
|
+
armature_data,
|
|
814
|
+
traversing_human_bone_specification,
|
|
815
|
+
bpy_bone_name_to_human_bone_specification,
|
|
816
|
+
error_bpy_bone_names,
|
|
817
|
+
)
|
|
818
|
+
|
|
819
|
+
traversing_human_bone_specifications.extend(
|
|
820
|
+
traversing_human_bone_specification.children
|
|
821
|
+
)
|
|
822
|
+
|
|
823
|
+
|
|
824
|
+
def clear_expression_material_binds(context: Context) -> None:
|
|
825
|
+
for material in context.blend_data.materials:
|
|
826
|
+
mtoon1 = get_material_extension(material).mtoon1
|
|
827
|
+
if not mtoon1.enabled:
|
|
828
|
+
continue
|
|
829
|
+
for texture_info in mtoon1.all_texture_info():
|
|
830
|
+
khr_texture_transform = texture_info.extensions.khr_texture_transform
|
|
831
|
+
khr_texture_transform.set_texture_uv(
|
|
832
|
+
shader.UV_GROUP_EXPRESSION_UV_OFFSET_BIND_LABEL,
|
|
833
|
+
Vector((0, 0)),
|
|
834
|
+
)
|
|
835
|
+
khr_texture_transform.set_texture_uv(
|
|
836
|
+
shader.UV_GROUP_EXPRESSION_UV_SCALE_BIND_LABEL,
|
|
837
|
+
Vector((0, 0)),
|
|
838
|
+
)
|
|
839
|
+
|
|
840
|
+
mtoon1.set_value(
|
|
841
|
+
shader.OUTPUT_GROUP_NAME,
|
|
842
|
+
shader.OUTPUT_GROUP_EXPRESSION_COLOR_ALPHA_BIND_LABEL,
|
|
843
|
+
0,
|
|
844
|
+
)
|
|
845
|
+
for label in (
|
|
846
|
+
shader.OUTPUT_GROUP_EXPRESSION_COLOR_BIND_LABEL,
|
|
847
|
+
shader.OUTPUT_GROUP_EXPRESSION_EMISSION_COLOR_BIND_LABEL,
|
|
848
|
+
shader.OUTPUT_GROUP_EXPRESSION_SHADE_COLOR_BIND_LABEL,
|
|
849
|
+
shader.OUTPUT_GROUP_EXPRESSION_MATCAP_COLOR_BIND_LABEL,
|
|
850
|
+
shader.OUTPUT_GROUP_EXPRESSION_RIM_COLOR_BIND_LABEL,
|
|
851
|
+
shader.OUTPUT_GROUP_EXPRESSION_OUTLINE_COLOR_BIND_LABEL,
|
|
852
|
+
):
|
|
853
|
+
mtoon1.set_vector3(shader.OUTPUT_GROUP_NAME, label, Vector((0, 0, 0)))
|
|
854
|
+
|
|
855
|
+
|
|
856
|
+
T_co = TypeVar("T_co", covariant=True)
|
|
857
|
+
|
|
858
|
+
|
|
859
|
+
# The actual type does not take type arguments.
|
|
860
|
+
class CollectionPropertyProtocol(Protocol[T_co]):
|
|
861
|
+
def add(self) -> T_co: ...
|
|
862
|
+
|
|
863
|
+
def __len__(self) -> int: ...
|
|
864
|
+
|
|
865
|
+
def __iter__(self) -> Iterator[T_co]: ...
|
|
866
|
+
|
|
867
|
+
def clear(self) -> None: ...
|
|
868
|
+
|
|
869
|
+
@overload
|
|
870
|
+
def __getitem__(self, index: int) -> T_co: ...
|
|
871
|
+
|
|
872
|
+
@overload
|
|
873
|
+
def __getitem__(
|
|
874
|
+
self, index: "slice[Optional[int], Optional[int], Optional[int]]"
|
|
875
|
+
) -> tuple[T_co, ...]: ...
|
|
876
|
+
|
|
877
|
+
def remove(self, index: int) -> None: ...
|
|
878
|
+
|
|
879
|
+
def values(self) -> ValuesView[T_co]: ...
|
|
880
|
+
|
|
881
|
+
def __contains__(self, value: str) -> bool: ...
|
|
882
|
+
|
|
883
|
+
def move(self, from_index: int, to_index: int) -> None: ...
|
|
884
|
+
|
|
885
|
+
|
|
886
|
+
def clear_global_variables() -> None:
|
|
887
|
+
BonePropertyGroup.armature_data_name_and_bone_uuid_to_bone_name_cache.clear()
|
|
888
|
+
HumanoidStructureBonePropertyGroup.pointer_to_bone_name_candidates.clear()
|