Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 37b92ded6d | |||
| fb470f19da | |||
| 843147db69 | |||
| fe2b0d50cb | |||
| c4dca2455d |
@@ -0,0 +1,79 @@
|
||||
# Fix for Garbled Japanese/Non-ASCII Text in Dropdowns
|
||||
|
||||
## Problem
|
||||
Japanese, Korean, Chinese, and other non-ASCII characters were displaying as garbled/corrupted text in dropdown menus for:
|
||||
- Armature selection in Quick Access panel
|
||||
- Mesh selection in Visemes panel
|
||||
|
||||
This is a known issue with Blender's EnumProperty system when using dynamic callbacks that return Unicode strings.
|
||||
|
||||
## Root Cause
|
||||
Blender's EnumProperty RNA system can have encoding issues when:
|
||||
1. The enum items function is called multiple times with changing data
|
||||
2. Unicode strings in display names aren't properly cached
|
||||
3. The internal C API receives the same Python string object in different states
|
||||
|
||||
## Solution
|
||||
Implemented proper caching with invalidation for EnumProperty items:
|
||||
|
||||
### Changes Made
|
||||
|
||||
1. **core/common.py** - Enhanced `get_armature_list()` function
|
||||
- Added cache key based on (name, pointer) tuples
|
||||
- Cache is invalidated only when actual objects change
|
||||
- Prevents Blender from re-encoding strings on every access
|
||||
- Added `clear_enum_caches()` helper function
|
||||
|
||||
2. **core/properties.py** - Enhanced `get_mesh_objects()` function
|
||||
- Added same caching mechanism as armature list
|
||||
- Cache key based on mesh objects (name, pointer)
|
||||
- Stable cache prevents encoding corruption
|
||||
|
||||
3. **core/common.py** - `get_mesh_from_identifier()` helper
|
||||
- Converts safe identifier back to mesh object
|
||||
- Handles both new format (`MESH_{pointer}`) and legacy format
|
||||
- Returns None if mesh not found
|
||||
|
||||
4. **ui/visemes_panel.py** - Updated mesh retrieval
|
||||
- Uses `get_mesh_from_identifier()` instead of direct lookup
|
||||
|
||||
5. **functions/visemes.py** - Updated all mesh access points
|
||||
- All operators now use the helper function consistently
|
||||
|
||||
## Technical Details
|
||||
|
||||
### ASCII-Safe Identifiers
|
||||
- Dropdown identifier: `ARM_{memory_pointer}` or `MESH_{memory_pointer}` (ASCII-safe, unique)
|
||||
- Dropdown display: Original object name (preserves Unicode characters)
|
||||
- Backwards compatibility: Falls back to direct name lookup
|
||||
|
||||
### Caching Strategy
|
||||
The cache uses function attributes to store:
|
||||
- `_cache_key`: Tuple of (name, pointer) for all relevant objects
|
||||
- `_cached_items`: The actual list of enum items
|
||||
|
||||
Cache is invalidated when:
|
||||
- Objects are added/removed
|
||||
- Objects are renamed
|
||||
- Object pointers change (object recreated)
|
||||
|
||||
This ensures Blender's RNA system receives the exact same Python string objects on subsequent calls, preventing encoding corruption.
|
||||
|
||||
## Testing
|
||||
|
||||
To verify the fix works:
|
||||
1. Create armature/mesh objects with Japanese/Korean/Chinese names (e.g., "アバター", "아바타", "化身")
|
||||
2. Open Quick Access panel - armature dropdown should display correctly
|
||||
3. Open Visemes panel - mesh dropdown should display correctly
|
||||
4. Select items - operations should work with the selected objects
|
||||
5. Rename objects - dropdowns should update and still display correctly
|
||||
|
||||
## Related Files
|
||||
- `core/properties.py` - Property definitions and mesh enumeration
|
||||
- `core/common.py` - Common utility functions and armature enumeration
|
||||
- `ui/visemes_panel.py` - Visemes UI panel
|
||||
- `ui/quick_access_panel.py` - Quick Access UI panel
|
||||
- `functions/visemes.py` - Viseme operators
|
||||
|
||||
## Note on prop_search
|
||||
The `prop_search` widget used for shape key/bone selection inherently handles non-ASCII characters correctly since it searches Blender's internal data structures directly, not custom enum properties.
|
||||
@@ -3,7 +3,7 @@
|
||||
schema_version = "1.0.0"
|
||||
|
||||
id = "avatar_toolkit"
|
||||
version = "0.6.0"
|
||||
version = "0.5.2"
|
||||
name = "Avatar Toolkit"
|
||||
tagline = "A modern tool for importing and optimizing models for VRChat, Resonite, and other similar games."
|
||||
maintainer = "Team NekoNeo"
|
||||
|
||||
@@ -174,130 +174,6 @@ physics_names: Dict[str, List[str]] = {
|
||||
"breast_tip": ["胸先", "むねさき", "ブレストティップ", "breasttip"],
|
||||
}
|
||||
|
||||
# MMD bone name patterns (for detection)
|
||||
mmd_bone_patterns: List[str] = [
|
||||
# Japanese bone names
|
||||
'全ての親', 'センター', '上半身', '下半身', '首', '頭',
|
||||
'右腕', '左腕', '右ひじ', '左ひじ', '右手首', '左手首',
|
||||
'右足', '左足', '右ひざ', '左ひざ', '右足首', '左足首',
|
||||
'両目', '左目', '右目', '右肩', '左肩',
|
||||
# English bone names (common in MMD exports)
|
||||
'center', 'groove', 'waist', 'upperbody', 'upperbody2', 'lowerbody',
|
||||
'neck', 'head',
|
||||
'shoulder_r', 'shoulder_l', 'arm_r', 'arm_l',
|
||||
'elbow_r', 'elbow_l', 'wrist_r', 'wrist_l',
|
||||
'leg_r', 'leg_l', 'knee_r', 'knee_l',
|
||||
'ankle_r', 'ankle_l', 'toe_r', 'toe_l',
|
||||
# Mixed/Romanized patterns
|
||||
'센터', 'グルーブ', 'ウエスト',
|
||||
# Common MMD suffixes
|
||||
'_r', '_l', '.r', '.l'
|
||||
]
|
||||
|
||||
# MMD to Unity bone mapping
|
||||
# Maps MMD bone names (after English translation) to Unity humanoid bone names
|
||||
mmd_to_unity_bone_map: Dict[str, Optional[str]] = {
|
||||
# Root and core
|
||||
"ParentNode": None, # Remove this
|
||||
"Center": "Hips",
|
||||
"センター": "Hips",
|
||||
"Groove": None, # Remove this
|
||||
"グルーブ": None,
|
||||
"Waist": None, # Will be merged into Hips
|
||||
|
||||
# Spine chain
|
||||
"LowerBody": "Hips",
|
||||
"下半身": "Hips",
|
||||
"UpperBody": "Spine",
|
||||
"上半身": "Spine",
|
||||
"UpperBody2": "Chest",
|
||||
"上半身2": "Chest",
|
||||
"Neck": "Neck",
|
||||
"首": "Neck",
|
||||
"Head": "Head",
|
||||
"頭": "Head",
|
||||
|
||||
# Right leg
|
||||
"RightLeg": "Right leg",
|
||||
"右足": "Right leg",
|
||||
"RightLegD": None, # Remove D variant
|
||||
"RightKnee": "Right knee",
|
||||
"右ひざ": "Right knee",
|
||||
"RightAnkle": "Right ankle",
|
||||
"右足首": "Right ankle",
|
||||
"RightToe": "Right toe",
|
||||
"右つま先": "Right toe",
|
||||
|
||||
# Left leg
|
||||
"LeftLeg": "Left leg",
|
||||
"左足": "Left leg",
|
||||
"LeftLegD": None, # Remove D variant
|
||||
"LeftKnee": "Left knee",
|
||||
"左ひざ": "Left knee",
|
||||
"LeftAnkle": "Left ankle",
|
||||
"左足首": "Left ankle",
|
||||
"LeftToe": "Left toe",
|
||||
"左つま先": "Left toe",
|
||||
|
||||
# Right arm
|
||||
"RightShoulder": "Right shoulder",
|
||||
"右肩": "Right shoulder",
|
||||
"RightArm": "Right arm",
|
||||
"右腕": "Right arm",
|
||||
"RightElbow": "Right elbow",
|
||||
"右ひじ": "Right elbow",
|
||||
"RightWrist": "Right wrist",
|
||||
"右手首": "Right wrist",
|
||||
|
||||
# Left arm
|
||||
"LeftShoulder": "Left shoulder",
|
||||
"左肩": "Left shoulder",
|
||||
"LeftArm": "Left arm",
|
||||
"左腕": "Left arm",
|
||||
"LeftElbow": "Left elbow",
|
||||
"左ひじ": "Left elbow",
|
||||
"LeftWrist": "Left wrist",
|
||||
"左手首": "Left wrist",
|
||||
|
||||
# Cancel/Helper bones (remove these)
|
||||
"WaistCancelRight": None,
|
||||
"WaistCancelLeft": None,
|
||||
"LegIKParentRight": None,
|
||||
"LegIKParentLeft": None,
|
||||
}
|
||||
|
||||
# Unity humanoid bone hierarchy
|
||||
# Defines parent-child relationships for Unity standard
|
||||
unity_bone_hierarchy: Dict[str, Optional[str]] = {
|
||||
"Hips": None, # Root bone
|
||||
"Spine": "Hips",
|
||||
"Chest": "Spine",
|
||||
"Neck": "Chest",
|
||||
"Head": "Neck",
|
||||
|
||||
# Arms
|
||||
"Left shoulder": "Chest",
|
||||
"Left arm": "Left shoulder",
|
||||
"Left elbow": "Left arm",
|
||||
"Left wrist": "Left elbow",
|
||||
|
||||
"Right shoulder": "Chest",
|
||||
"Right arm": "Right shoulder",
|
||||
"Right elbow": "Right arm",
|
||||
"Right wrist": "Right elbow",
|
||||
|
||||
# Legs
|
||||
"Left leg": "Hips",
|
||||
"Left knee": "Left leg",
|
||||
"Left ankle": "Left knee",
|
||||
"Left toe": "Left ankle",
|
||||
|
||||
"Right leg": "Hips",
|
||||
"Right knee": "Right leg",
|
||||
"Right ankle": "Right knee",
|
||||
"Right toe": "Right ankle",
|
||||
}
|
||||
|
||||
# Create reverse lookup dictionaries
|
||||
reverse_shapekey_lookup: Dict[str, str] = {}
|
||||
reverse_material_lookup: Dict[str, str] = {}
|
||||
|
||||
@@ -735,67 +735,6 @@ class AvatarToolkitSceneProperties(PropertyGroup):
|
||||
default=True
|
||||
)
|
||||
|
||||
# MMD Conversion Properties
|
||||
mmd_make_parent: BoolProperty(
|
||||
name=t("MMD.make_armature_parent"),
|
||||
description="Remove parent Empty object and make armature the main parent",
|
||||
default=True
|
||||
)
|
||||
|
||||
mmd_rename_armature: BoolProperty(
|
||||
name=t("MMD.rename_to_armature"),
|
||||
description="Rename the armature object to 'Armature'",
|
||||
default=True
|
||||
)
|
||||
|
||||
mmd_translate_names: BoolProperty(
|
||||
name=t("MMD.translate_names"),
|
||||
description="Translate Japanese names to English using MMD dictionary and translation services",
|
||||
default=True
|
||||
)
|
||||
|
||||
mmd_translate_bones: BoolProperty(
|
||||
name=t("MMD.translate_bones"),
|
||||
description="Translate bone names",
|
||||
default=True
|
||||
)
|
||||
|
||||
mmd_translate_materials: BoolProperty(
|
||||
name=t("MMD.translate_materials"),
|
||||
description="Translate material names",
|
||||
default=True
|
||||
)
|
||||
|
||||
mmd_translate_shapekeys: BoolProperty(
|
||||
name=t("MMD.translate_shapekeys"),
|
||||
description="Translate shape key names",
|
||||
default=True
|
||||
)
|
||||
|
||||
mmd_translate_objects: BoolProperty(
|
||||
name=t("MMD.translate_objects"),
|
||||
description="Translate object names",
|
||||
default=True
|
||||
)
|
||||
|
||||
mmd_restructure_bones: BoolProperty(
|
||||
name=t("MMD.restructure_bones"),
|
||||
description="Restructure bone hierarchy to Unity humanoid format (Hips, Spine, Chest, etc.)",
|
||||
default=True
|
||||
)
|
||||
|
||||
mmd_remove_twist_bones: BoolProperty(
|
||||
name=t("MMD.remove_twist_bones"),
|
||||
description="Remove twist bones",
|
||||
default=True
|
||||
)
|
||||
|
||||
mmd_remove_zero_weight_bones: BoolProperty(
|
||||
name=t("MMD.remove_zero_weight_bones"),
|
||||
description="Remove bones with zero or near-zero vertex weights",
|
||||
default=False
|
||||
)
|
||||
|
||||
# Translation System Properties
|
||||
translation_service: EnumProperty(
|
||||
name=t("Translation.service"),
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ GITHUB_REPO = "teamneoneko/Avatar-Toolkit"
|
||||
# Define which version series this installation can update to
|
||||
# For example: ["0.1"] means only look for 0.1.x updates
|
||||
# ["0.2", "0.3"] would look for both 0.2.x and 0.3.x
|
||||
ALLOWED_VERSION_SERIES = ["0.6"]
|
||||
ALLOWED_VERSION_SERIES = ["0.5"]
|
||||
|
||||
is_checking_for_update: bool = False
|
||||
update_needed: bool = False
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authors": ["Avatar Toolkit Team"],
|
||||
"messages": {
|
||||
"AvatarToolkit.label": "Avatar Toolkit (Alpha 0.6.0)",
|
||||
"AvatarToolkit.label": "Avatar Toolkit (Alpha 0.5.2)",
|
||||
"AvatarToolkit.desc1": "Avatar Toolkit is in Early Access there",
|
||||
"AvatarToolkit.desc2": "will be issues, if you find any issues,",
|
||||
"AvatarToolkit.desc3": "please report it on our Github.",
|
||||
@@ -601,79 +601,6 @@
|
||||
"VRM.remove_root": "Remove Root Bone",
|
||||
"VRM.remove_root_desc": "Remove unnecessary VRM root bone and make Hips the root bone",
|
||||
|
||||
"MMD.panel.label": "MMD Converter",
|
||||
"MMD.converter.title": "MMD Armature Converter",
|
||||
"MMD.no_armature_selected": "No armature selected",
|
||||
"MMD.select_armature_to_convert": "Select an armature to convert",
|
||||
"MMD.armature_name": "Armature: {name}",
|
||||
"MMD.armature_detected": "MMD armature detected",
|
||||
"MMD.no_mmd_bones_detected": "No MMD bones detected",
|
||||
"MMD.not_mmd_armature": "Selected armature does not appear to be MMD format",
|
||||
"MMD.make_armature_parent": "Make Armature Main Parent",
|
||||
"MMD.rename_to_armature": "Rename to 'Armature'",
|
||||
"MMD.translate_names": "Translate Names to English",
|
||||
"MMD.translate_bones": "Bones",
|
||||
"MMD.translate_materials": "Materials",
|
||||
"MMD.translate_shapekeys": "Shape Keys",
|
||||
"MMD.translate_objects": "Objects",
|
||||
"MMD.restructure_bones": "Restructure to Unity Format",
|
||||
"MMD.bone_cleanup": "Bone Cleanup Options:",
|
||||
"MMD.remove_ik_bones": "Remove IK Bones",
|
||||
"MMD.remove_twist_bones": "Remove Twist Bones",
|
||||
"MMD.remove_zero_weight_bones": "Remove Zero Weight Bones",
|
||||
"MMD.translation_options": "Translation Options:",
|
||||
"MMD.convert_armature_button": "Convert MMD Armature",
|
||||
"MMD.convert_armature.label": "Convert MMD Armature",
|
||||
"MMD.convert_armature.desc": "Convert MMD armature to standard Blender format",
|
||||
"MMD.conversion_info.title": "Conversion Info:",
|
||||
"MMD.conversion_info.removes_parent": "• Removes parent Empty object",
|
||||
"MMD.conversion_info.renames_armature": "• Renames armature to 'Armature'",
|
||||
"MMD.conversion_info.restructures_bones": "• Converts to Unity bone structure (Hips/Spine/Chest)",
|
||||
"MMD.conversion_info.removes_ik_bones": "• Removes IK (Inverse Kinematics) bones",
|
||||
"MMD.conversion_info.removes_twist_bones": "• Removes twist bones",
|
||||
"MMD.conversion_info.removes_zero_weight_bones": "• Removes bones with zero vertex weights",
|
||||
"MMD.conversion_info.maintains_hierarchy": "• Maintains object hierarchy",
|
||||
"MMD.conversion_info.translates_names": "• Translates Japanese names to English",
|
||||
"MMD.detection_failed.title": "MMD Detection Failed:",
|
||||
"MMD.detection_failed.not_mmd_format": "• Selected armature is not MMD format",
|
||||
"MMD.detection_failed.need_mmd_bones": "• Need at least 5 MMD bones detected",
|
||||
"MMD.detection_failed.check_bone_names": "• Check armature bone names",
|
||||
"MMD.error.invalid_armature": "Invalid armature object",
|
||||
"MMD.error.not_mmd_armature": "Armature does not appear to be MMD format",
|
||||
"MMD.error.rename_failed": "Failed to rename armature: {error}",
|
||||
"MMD.armature_already_root": "Armature already has no parent",
|
||||
"MMD.armature_already_named": "Armature is already named 'Armature'",
|
||||
"MMD.parent_removed_and_reparented": "Removed parent '{parent_name}' and reparented {count} objects to armature",
|
||||
"MMD.parent_unlinked_and_reparented": "Unlinked from parent '{parent_name}' and reparented {count} objects",
|
||||
"MMD.parent_unlinked": "Unlinked armature from parent '{parent_name}'",
|
||||
"MMD.armature_renamed": "Renamed armature from '{old_name}' to '{new_name}'",
|
||||
"MMD.armature_renamed_with_suffix": "Renamed armature from '{old_name}' to '{new_name}' (name collision)",
|
||||
"MMD.conversion_complete": "MMD armature conversion completed successfully",
|
||||
"MMD.translation_starting": "Starting name translation...",
|
||||
"MMD.bones_translated": "Translated {count} bones",
|
||||
"MMD.bones_failed": "Failed to translate {count} bones",
|
||||
"MMD.materials_translated": "Translated {count} materials",
|
||||
"MMD.materials_failed": "Failed to translate {count} materials",
|
||||
"MMD.shapekeys_translated": "Translated {count} shape keys",
|
||||
"MMD.shapekeys_failed": "Failed to translate {count} shape keys",
|
||||
"MMD.objects_translated": "Translated {count} objects",
|
||||
"MMD.objects_failed": "Failed to translate {count} objects",
|
||||
"MMD.translation_complete": "Translation complete: {total} items translated",
|
||||
"MMD.restructure_starting": "Restructuring bones to Unity format...",
|
||||
"MMD.bones_restructured": "Restructured {count} bones to Unity format",
|
||||
"MMD.bones_removed": "Removed {count} unnecessary bones",
|
||||
"MMD.bones_reparented": "Reparented {count} bones",
|
||||
"MMD.restructure_failed": "Bone restructuring failed: {error}",
|
||||
"MMD.ik_bones_removed": "Removed {count} IK bones",
|
||||
"MMD.no_ik_bones_found": "No IK bones found to remove",
|
||||
"MMD.ik_removal_failed": "IK bone removal failed: {error}",
|
||||
"MMD.twist_bones_removed": "Removed {count} twist bones",
|
||||
"MMD.no_twist_bones_found": "No twist bones found to remove",
|
||||
"MMD.twist_removal_failed": "Twist bone removal failed: {error}",
|
||||
"MMD.zero_weight_bones_removed": "Removed {count} zero weight bones",
|
||||
"MMD.no_zero_weight_bones_found": "No zero weight bones found to remove",
|
||||
"MMD.zero_weight_removal_failed": "Zero weight bone removal failed: {error}",
|
||||
|
||||
"Translation.label": "Translation",
|
||||
"Translation.service": "Translation Service",
|
||||
"Translation.service_desc": "Choose the translation service to use",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authors": ["Avatar Toolkit Team"],
|
||||
"messages": {
|
||||
"AvatarToolkit.label": "アバターツールキット (アルファ 0.6.0)",
|
||||
"AvatarToolkit.label": "アバターツールキット (アルファ 0.5.2)",
|
||||
"AvatarToolkit.desc1": "アバターツールキットは早期アクセス中であり、",
|
||||
"AvatarToolkit.desc2": "問題が発生する可能性があります。問題を見つけた場合は、",
|
||||
"AvatarToolkit.desc3": "GitHubで報告してください。",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authors": ["Avatar Toolkit Team"],
|
||||
"messages": {
|
||||
"AvatarToolkit.label": "아바타 툴킷 (알파 0.6.0)",
|
||||
"AvatarToolkit.label": "아바타 툴킷 (알파 0.5.2)",
|
||||
"AvatarToolkit.desc1": "아바타 툴킷은 초기 액세스 단계에 있으므로",
|
||||
"AvatarToolkit.desc2": "문제가 있을 수 있습니다. 문제를 발견하시면",
|
||||
"AvatarToolkit.desc3": "Github에 보고해 주세요.",
|
||||
|
||||
+1
-4
@@ -14,8 +14,7 @@ VISEMES_ORDER = 6
|
||||
EYE_TRACKING_ORDER = 7
|
||||
TEXTURE_ATLAS_ORDER = 8
|
||||
VRM_UNITY_ORDER = 9
|
||||
MMD_ORDER = 10
|
||||
SETTINGS_ORDER = 11
|
||||
SETTINGS_ORDER = 10
|
||||
|
||||
# Panel open/closed by default
|
||||
PANELS_OPEN_BY_DEFAULT = {
|
||||
@@ -28,7 +27,6 @@ PANELS_OPEN_BY_DEFAULT = {
|
||||
'EYE_TRACKING': True,
|
||||
'TEXTURE_ATLAS': True,
|
||||
'VRM_UNITY': True,
|
||||
'MMD': True,
|
||||
'SETTINGS': True,
|
||||
'TRANSLATION': True,
|
||||
}
|
||||
@@ -46,7 +44,6 @@ def get_panel_order(panel_name: str) -> int:
|
||||
'eye_tracking': EYE_TRACKING_ORDER,
|
||||
'texture_atlas': TEXTURE_ATLAS_ORDER,
|
||||
'vrm_unity': VRM_UNITY_ORDER,
|
||||
'mmd': MMD_ORDER,
|
||||
'settings': SETTINGS_ORDER,
|
||||
}
|
||||
return order_map.get(panel_name.lower(), 99)
|
||||
|
||||
Reference in New Issue
Block a user