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:
59
common/test_helper.py
Normal file
59
common/test_helper.py
Normal file
@@ -0,0 +1,59 @@
|
||||
# SPDX-License-Identifier: MIT OR GPL-3.0-or-later
|
||||
from typing import Optional
|
||||
from unittest import TestCase
|
||||
|
||||
import bpy
|
||||
|
||||
from .blender_manifest import BlenderManifest
|
||||
|
||||
DEVELOPMENT_MODULE = ".".join(__name__.split(".")[:-2])
|
||||
MANIFEST_ID = BlenderManifest.read().id
|
||||
|
||||
|
||||
def make_test_method_name(text: str) -> str:
|
||||
replaced_chars: list[str] = []
|
||||
for char in text:
|
||||
if char == "«":
|
||||
replaced_chars.append("««")
|
||||
elif char == "»":
|
||||
replaced_chars.append("»»")
|
||||
elif ("_" + char).isidentifier():
|
||||
replaced_chars.append(char)
|
||||
else:
|
||||
replaced_chars.append(f"«{ord(char):x}»")
|
||||
return "test_" + ("".join(replaced_chars))
|
||||
|
||||
|
||||
class AddonTestCase(TestCase):
|
||||
disabled_installed_module: Optional[str]
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls) -> None:
|
||||
super().setUpClass()
|
||||
|
||||
cls.disabled_installed_module = None
|
||||
for addon in bpy.context.preferences.addons:
|
||||
module = addon.module
|
||||
if not module.endswith("." + MANIFEST_ID):
|
||||
continue
|
||||
bpy.ops.preferences.addon_disable(module=module)
|
||||
cls.disabled_installed_module = module
|
||||
|
||||
def setUp(self) -> None:
|
||||
super().setUp()
|
||||
bpy.ops.preferences.addon_enable(module=DEVELOPMENT_MODULE)
|
||||
bpy.ops.wm.read_homefile(use_empty=True)
|
||||
|
||||
def tearDown(self) -> None:
|
||||
super().tearDown()
|
||||
bpy.ops.preferences.addon_disable(module=DEVELOPMENT_MODULE)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls) -> None:
|
||||
super().tearDownClass()
|
||||
|
||||
disabled_installed_module = cls.disabled_installed_module
|
||||
if disabled_installed_module is not None:
|
||||
bpy.ops.preferences.addon_enable(module=disabled_installed_module)
|
||||
|
||||
cls.disabled_installed_module = None
|
||||
Reference in New Issue
Block a user