feat: Add VRM Blender addon with complete import/export functionality

- Add core VRM addon infrastructure with manifest and registration
- Add common utilities module with file system, logging, and conversion helpers
- Add human bone mapper with support for multiple rigging standards (Mixamo, MMD, Unreal, Rigify, etc.)
- Add VRM 0.x and 1.x format support with property groups and handlers
- Add editor UI panels for VRM metadata, spring bones, and MToon materials
- Add exporter with glTF2 extension support for VRM format serialization
- Add importer with scene reconstruction and armature generation
- Add MToon shader support with auto-setup and material migration
- Add spring bone physics simulation with constraint handling
- Add node constraint editor for advanced rigging control
- Add comprehensive validation and error handling with user dialogs
- Add scene watcher for real-time property synchronization
- Add workspace management and preference system
- Include Python cache files and Blender manifest configuration
- This is the initial commit establishing the complete VRM addon ecosystem for Blender
This commit is contained in:
2026-01-01 14:21:56 +08:00
commit 091ad6a49a
243 changed files with 60636 additions and 0 deletions

10
common/ops/__init__.py Normal file
View File

@@ -0,0 +1,10 @@
# SPDX-License-Identifier: MIT OR GPL-3.0-or-later
from . import export_scene, icyp, import_scene, vrm, wm
__all__ = [
"export_scene",
"icyp",
"import_scene",
"vrm",
"wm",
]

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,67 @@
# This code is auto generated.
# To regenerate, run the `uv run tools/property_typing.py` command.
from collections.abc import Mapping, Sequence
from typing import Optional, Union
import bpy
# This code is auto generated.
# To regenerate, run the `uv run tools/property_typing.py` command.
def vrm(
execution_context: str = "EXEC_DEFAULT",
/,
*,
filter_glob: str = "*.vrm",
use_addon_preferences: bool = False,
export_invisibles: bool = False,
export_only_selections: bool = False,
enable_advanced_preferences: bool = False,
export_all_influences: bool = False,
export_lights: bool = False,
export_gltf_animations: bool = False,
export_try_sparse_sk: bool = False,
errors: Optional[Sequence[Mapping[str, Union[str, int, float, bool]]]] = None,
armature_object_name: str = "",
ignore_warning: bool = False,
filepath: str = "",
check_existing: bool = True,
) -> set[str]:
return bpy.ops.export_scene.vrm( # type: ignore[attr-defined, no-any-return]
execution_context,
filter_glob=filter_glob,
use_addon_preferences=use_addon_preferences,
export_invisibles=export_invisibles,
export_only_selections=export_only_selections,
enable_advanced_preferences=enable_advanced_preferences,
export_all_influences=export_all_influences,
export_lights=export_lights,
export_gltf_animations=export_gltf_animations,
export_try_sparse_sk=export_try_sparse_sk,
errors=errors if errors is not None else [],
armature_object_name=armature_object_name,
ignore_warning=ignore_warning,
filepath=filepath,
check_existing=check_existing,
)
# This code is auto generated.
# To regenerate, run the `uv run tools/property_typing.py` command.
def vrma(
execution_context: str = "EXEC_DEFAULT",
/,
*,
filter_glob: str = "*.vrma",
armature_object_name: str = "",
filepath: str = "",
check_existing: bool = True,
) -> set[str]:
return bpy.ops.export_scene.vrma( # type: ignore[attr-defined, no-any-return]
execution_context,
filter_glob=filter_glob,
armature_object_name=armature_object_name,
filepath=filepath,
check_existing=check_existing,
)

53
common/ops/icyp.py Normal file
View File

@@ -0,0 +1,53 @@
# This code is auto generated.
# To regenerate, run the `uv run tools/property_typing.py` command.
import bpy
# This code is auto generated.
# To regenerate, run the `uv run tools/property_typing.py` command.
def make_basic_armature(
execution_context: str = "EXEC_DEFAULT",
/,
*,
skip_heavy_armature_setup: bool = False,
wip_with_template_mesh: bool = False,
tall: float = 1.7,
head_ratio: float = 8.0,
head_width_ratio: float = 0.6666666666666666,
aging_ratio: float = 0.5,
eye_depth: float = -0.03,
shoulder_in_width: float = 0.05,
shoulder_width: float = 0.08,
arm_length_ratio: float = 1,
hand_ratio: float = 1,
finger_1_2_ratio: float = 0.75,
finger_2_3_ratio: float = 0.75,
nail_bone: bool = False,
leg_length_ratio: float = 0.5,
leg_width_ratio: float = 1,
leg_size: float = 0.26,
custom_property_name: str = "",
) -> set[str]:
return bpy.ops.icyp.make_basic_armature( # type: ignore[attr-defined, no-any-return]
execution_context,
skip_heavy_armature_setup=skip_heavy_armature_setup,
wip_with_template_mesh=wip_with_template_mesh,
tall=tall,
head_ratio=head_ratio,
head_width_ratio=head_width_ratio,
aging_ratio=aging_ratio,
eye_depth=eye_depth,
shoulder_in_width=shoulder_in_width,
shoulder_width=shoulder_width,
arm_length_ratio=arm_length_ratio,
hand_ratio=hand_ratio,
finger_1_2_ratio=finger_1_2_ratio,
finger_2_3_ratio=finger_2_3_ratio,
nail_bone=nail_bone,
leg_length_ratio=leg_length_ratio,
leg_width_ratio=leg_width_ratio,
leg_size=leg_size,
custom_property_name=custom_property_name,
)

View File

@@ -0,0 +1,57 @@
# This code is auto generated.
# To regenerate, run the `uv run tools/property_typing.py` command.
import bpy
# This code is auto generated.
# To regenerate, run the `uv run tools/property_typing.py` command.
def vrm(
execution_context: str = "EXEC_DEFAULT",
/,
*,
filter_glob: str = "*.vrm",
use_addon_preferences: bool = False,
extract_textures_into_folder: bool = False,
make_new_texture_folder: bool = True,
set_shading_type_to_material_on_import: bool = True,
set_view_transform_to_standard_on_import: bool = True,
set_armature_display_to_wire: bool = True,
set_armature_display_to_show_in_front: bool = True,
set_armature_bone_shape_to_default: bool = True,
enable_mtoon_outline_preview: bool = True,
filepath: str = "",
) -> set[str]:
return bpy.ops.import_scene.vrm( # type: ignore[attr-defined, no-any-return]
execution_context,
filter_glob=filter_glob,
use_addon_preferences=use_addon_preferences,
extract_textures_into_folder=extract_textures_into_folder,
make_new_texture_folder=make_new_texture_folder,
set_shading_type_to_material_on_import=set_shading_type_to_material_on_import,
set_view_transform_to_standard_on_import=set_view_transform_to_standard_on_import,
set_armature_display_to_wire=set_armature_display_to_wire,
set_armature_display_to_show_in_front=set_armature_display_to_show_in_front,
set_armature_bone_shape_to_default=set_armature_bone_shape_to_default,
enable_mtoon_outline_preview=enable_mtoon_outline_preview,
filepath=filepath,
)
# This code is auto generated.
# To regenerate, run the `uv run tools/property_typing.py` command.
def vrma(
execution_context: str = "EXEC_DEFAULT",
/,
*,
filter_glob: str = "*.vrma",
armature_object_name: str = "",
filepath: str = "",
) -> set[str]:
return bpy.ops.import_scene.vrma( # type: ignore[attr-defined, no-any-return]
execution_context,
filter_glob=filter_glob,
armature_object_name=armature_object_name,
filepath=filepath,
)

1890
common/ops/vrm.py Normal file

File diff suppressed because it is too large Load Diff

165
common/ops/wm.py Normal file
View File

@@ -0,0 +1,165 @@
# This code is auto generated.
# To regenerate, run the `uv run tools/property_typing.py` command.
from collections.abc import Mapping, Sequence
from typing import Optional, Union
import bpy
# This code is auto generated.
# To regenerate, run the `uv run tools/property_typing.py` command.
def vrm_gltf2_addon_disabled_warning(
execution_context: str = "EXEC_DEFAULT",
) -> set[str]:
return bpy.ops.wm.vrm_gltf2_addon_disabled_warning( # type: ignore[attr-defined, no-any-return]
execution_context,
)
# This code is auto generated.
# To regenerate, run the `uv run tools/property_typing.py` command.
def vrm_export_human_bones_assignment(
execution_context: str = "EXEC_DEFAULT",
/,
*,
armature_object_name: str = "",
) -> set[str]:
return bpy.ops.wm.vrm_export_human_bones_assignment( # type: ignore[attr-defined, no-any-return]
execution_context,
armature_object_name=armature_object_name,
)
# This code is auto generated.
# To regenerate, run the `uv run tools/property_typing.py` command.
def vrm_export_confirmation(
execution_context: str = "EXEC_DEFAULT",
/,
*,
errors: Optional[Sequence[Mapping[str, Union[str, int, float, bool]]]] = None,
armature_object_name: str = "",
export_anyway: bool = False,
) -> set[str]:
return bpy.ops.wm.vrm_export_confirmation( # type: ignore[attr-defined, no-any-return]
execution_context,
errors=errors if errors is not None else [],
armature_object_name=armature_object_name,
export_anyway=export_anyway,
)
# This code is auto generated.
# To regenerate, run the `uv run tools/property_typing.py` command.
def vrm_export_armature_selection(
execution_context: str = "EXEC_DEFAULT",
/,
*,
armature_object_name: str = "",
armature_object_name_candidates: Optional[
Sequence[Mapping[str, Union[str, int, float, bool]]]
] = None,
) -> set[str]:
return bpy.ops.wm.vrm_export_armature_selection( # type: ignore[attr-defined, no-any-return]
execution_context,
armature_object_name=armature_object_name,
armature_object_name_candidates=armature_object_name_candidates
if armature_object_name_candidates is not None
else [],
)
# This code is auto generated.
# To regenerate, run the `uv run tools/property_typing.py` command.
def vrma_export_prerequisite(
execution_context: str = "EXEC_DEFAULT",
/,
*,
armature_object_name: str = "",
armature_object_name_candidates: Optional[
Sequence[Mapping[str, Union[str, int, float, bool]]]
] = None,
) -> set[str]:
return bpy.ops.wm.vrma_export_prerequisite( # type: ignore[attr-defined, no-any-return]
execution_context,
armature_object_name=armature_object_name,
armature_object_name_candidates=armature_object_name_candidates
if armature_object_name_candidates is not None
else [],
)
# This code is auto generated.
# To regenerate, run the `uv run tools/property_typing.py` command.
def vrm_error_dialog(
execution_context: str = "EXEC_DEFAULT",
/,
*,
title: str = "",
lines: Optional[Sequence[Mapping[str, Union[str, int, float, bool]]]] = None,
active_line_index: int = 0,
) -> set[str]:
return bpy.ops.wm.vrm_error_dialog( # type: ignore[attr-defined, no-any-return]
execution_context,
title=title,
lines=lines if lines is not None else [],
active_line_index=active_line_index,
)
# This code is auto generated.
# To regenerate, run the `uv run tools/property_typing.py` command.
def vrm_license_warning(
execution_context: str = "EXEC_DEFAULT",
/,
*,
filepath: str = "",
license_confirmations: Optional[
Sequence[Mapping[str, Union[str, int, float, bool]]]
] = None,
import_anyway: bool = False,
extract_textures_into_folder: bool = False,
make_new_texture_folder: bool = False,
set_shading_type_to_material_on_import: bool = False,
set_view_transform_to_standard_on_import: bool = False,
set_armature_display_to_wire: bool = False,
set_armature_display_to_show_in_front: bool = False,
set_armature_bone_shape_to_default: bool = False,
enable_mtoon_outline_preview: bool = False,
) -> set[str]:
return bpy.ops.wm.vrm_license_warning( # type: ignore[attr-defined, no-any-return]
execution_context,
filepath=filepath,
license_confirmations=license_confirmations
if license_confirmations is not None
else [],
import_anyway=import_anyway,
extract_textures_into_folder=extract_textures_into_folder,
make_new_texture_folder=make_new_texture_folder,
set_shading_type_to_material_on_import=set_shading_type_to_material_on_import,
set_view_transform_to_standard_on_import=set_view_transform_to_standard_on_import,
set_armature_display_to_wire=set_armature_display_to_wire,
set_armature_display_to_show_in_front=set_armature_display_to_show_in_front,
set_armature_bone_shape_to_default=set_armature_bone_shape_to_default,
enable_mtoon_outline_preview=enable_mtoon_outline_preview,
)
# This code is auto generated.
# To regenerate, run the `uv run tools/property_typing.py` command.
def vrma_import_prerequisite(
execution_context: str = "EXEC_DEFAULT",
/,
*,
armature_object_name: str = "",
armature_object_name_candidates: Optional[
Sequence[Mapping[str, Union[str, int, float, bool]]]
] = None,
) -> set[str]:
return bpy.ops.wm.vrma_import_prerequisite( # type: ignore[attr-defined, no-any-return]
execution_context,
armature_object_name=armature_object_name,
armature_object_name_candidates=armature_object_name_candidates
if armature_object_name_candidates is not None
else [],
)