Fix garbled Japanese/Unicode text in armature and mesh dropdowns
- Add proper caching to EnumProperty callbacks to prevent encoding corruption - Use ASCII-safe identifiers (ARM_/MESH_ + pointer) with Unicode display names - Add get_mesh_from_identifier() helper for safe mesh retrieval - Update visemes panel to use new mesh identifier system - Ensure stable string objects prevent Blender RNA encoding issues
This commit is contained in:
+60
-6
@@ -142,6 +142,41 @@ def set_active_armature(context: Context, armature: Object) -> None:
|
||||
else:
|
||||
context.scene.avatar_toolkit.active_armature = 'NONE'
|
||||
|
||||
def get_mesh_from_identifier(mesh_id: str) -> Optional[Object]:
|
||||
"""Get mesh object from safe identifier
|
||||
|
||||
Args:
|
||||
mesh_id: Safe identifier in format "MESH_{pointer}" or direct object name
|
||||
|
||||
Returns:
|
||||
Mesh object or None if not found
|
||||
"""
|
||||
if not mesh_id or mesh_id == 'NONE':
|
||||
return None
|
||||
|
||||
# Handle new-style identifiers (MESH_{pointer})
|
||||
if mesh_id.startswith('MESH_'):
|
||||
try:
|
||||
pointer_str = mesh_id[5:] # Remove "MESH_" prefix
|
||||
target_pointer = int(pointer_str)
|
||||
|
||||
# Search for object with matching pointer
|
||||
for obj in bpy.data.objects:
|
||||
if obj.type == 'MESH' and obj.as_pointer() == target_pointer:
|
||||
return obj
|
||||
except (ValueError, AttributeError):
|
||||
pass
|
||||
|
||||
# Fallback for old-style identifiers (direct name)
|
||||
return bpy.data.objects.get(mesh_id)
|
||||
|
||||
def clear_enum_caches() -> None:
|
||||
"""Clear all enum property caches to force refresh of dropdown lists"""
|
||||
if hasattr(get_armature_list, '_cache_key'):
|
||||
delattr(get_armature_list, '_cache_key')
|
||||
if hasattr(get_armature_list, '_cached_items'):
|
||||
delattr(get_armature_list, '_cached_items')
|
||||
|
||||
def get_armature_list(self: Optional[Any] = None, context: Optional[Context] = None) -> List[Tuple[str, str, str]]:
|
||||
"""Get list of all armature objects in the scene
|
||||
|
||||
@@ -149,21 +184,40 @@ def get_armature_list(self: Optional[Any] = None, context: Optional[Context] = N
|
||||
- identifier: ASCII-safe unique ID (uses object's memory address)
|
||||
- display_name: The actual object name (can contain Japanese characters)
|
||||
- description: Empty string
|
||||
|
||||
Uses caching to prevent encoding issues with Blender's EnumProperty system
|
||||
"""
|
||||
if context is None:
|
||||
context = bpy.context
|
||||
|
||||
# Use object's as_pointer() value as a safe ASCII identifier
|
||||
# Create a cache key based on armature objects in scene
|
||||
armature_objects = [obj for obj in context.scene.objects if obj.type == 'ARMATURE']
|
||||
cache_key = tuple((obj.name, obj.as_pointer()) for obj in armature_objects)
|
||||
|
||||
# Check if we have a cached result
|
||||
if hasattr(get_armature_list, '_cache_key') and get_armature_list._cache_key == cache_key:
|
||||
if hasattr(get_armature_list, '_cached_items'):
|
||||
return get_armature_list._cached_items
|
||||
|
||||
# Build the list
|
||||
armatures = []
|
||||
for obj in context.scene.objects:
|
||||
if obj.type == 'ARMATURE':
|
||||
for obj in armature_objects:
|
||||
# Create a safe ASCII identifier using the object pointer
|
||||
safe_id = f"ARM_{obj.as_pointer()}"
|
||||
armatures.append((safe_id, obj.name, ""))
|
||||
# Use the name directly - Blender should handle Unicode in display names
|
||||
display_name = obj.name
|
||||
armatures.append((safe_id, display_name, ""))
|
||||
|
||||
if not armatures:
|
||||
return [('NONE', t("Armature.validation.no_armature"), '')]
|
||||
return armatures
|
||||
result = [('NONE', t("Armature.validation.no_armature"), '')]
|
||||
else:
|
||||
result = armatures
|
||||
|
||||
# Cache the result
|
||||
get_armature_list._cache_key = cache_key
|
||||
get_armature_list._cached_items = result
|
||||
|
||||
return result
|
||||
|
||||
def auto_select_single_armature(context: Context) -> None:
|
||||
"""Automatically select armature if only one exists in scene"""
|
||||
|
||||
+35
-3
@@ -67,10 +67,42 @@ def highlight_problem_bones(self: PropertyGroup, context: Context) -> None:
|
||||
save_preference("highlight_problem_bones", self.highlight_problem_bones)
|
||||
|
||||
def get_mesh_objects(self, context):
|
||||
meshes = [(obj.name, obj.name, "") for obj in bpy.data.objects if obj.type == 'MESH']
|
||||
"""Get list of all mesh objects with ASCII-safe identifiers
|
||||
|
||||
Returns tuples of (identifier, display_name, description) where:
|
||||
- identifier: ASCII-safe unique ID (uses object's memory address)
|
||||
- display_name: The actual object name (can contain Japanese/non-ASCII characters)
|
||||
- description: Empty string
|
||||
|
||||
Uses caching to prevent encoding issues with Blender's EnumProperty system
|
||||
"""
|
||||
# Create a cache key based on mesh objects
|
||||
mesh_objects = [obj for obj in bpy.data.objects if obj.type == 'MESH']
|
||||
cache_key = tuple((obj.name, obj.as_pointer()) for obj in mesh_objects)
|
||||
|
||||
# Check if we have a cached result
|
||||
if hasattr(get_mesh_objects, '_cache_key') and get_mesh_objects._cache_key == cache_key:
|
||||
if hasattr(get_mesh_objects, '_cached_items'):
|
||||
return get_mesh_objects._cached_items
|
||||
|
||||
# Build the list
|
||||
meshes = []
|
||||
for obj in mesh_objects:
|
||||
safe_id = f"MESH_{obj.as_pointer()}"
|
||||
# Use the name directly - Blender should handle Unicode in display names
|
||||
display_name = obj.name
|
||||
meshes.append((safe_id, display_name, ""))
|
||||
|
||||
if not meshes:
|
||||
return [('NONE', t("Visemes.no_meshes"), '')]
|
||||
return meshes
|
||||
result = [('NONE', t("Visemes.no_meshes"), '')]
|
||||
else:
|
||||
result = meshes
|
||||
|
||||
# Cache the result
|
||||
get_mesh_objects._cache_key = cache_key
|
||||
get_mesh_objects._cached_items = result
|
||||
|
||||
return result
|
||||
|
||||
def auto_populate_merge_armatures(context: Context) -> None:
|
||||
"""Auto-populate merge armature fields when there are 2+ armatures"""
|
||||
|
||||
@@ -137,15 +137,17 @@ class AvatarToolkit_OT_PreviewVisemes(Operator):
|
||||
return False
|
||||
|
||||
# Get mesh from UI selection
|
||||
from ..core.common import get_mesh_from_identifier
|
||||
props = context.scene.avatar_toolkit
|
||||
mesh_obj = bpy.data.objects.get(props.viseme_mesh)
|
||||
mesh_obj = get_mesh_from_identifier(props.viseme_mesh)
|
||||
|
||||
# Validate mesh
|
||||
return mesh_obj and mesh_obj.type == 'MESH'
|
||||
|
||||
def execute(self, context: Context) -> Set[str]:
|
||||
from ..core.common import get_mesh_from_identifier
|
||||
props = context.scene.avatar_toolkit
|
||||
mesh = bpy.data.objects.get(props.viseme_mesh)
|
||||
mesh = get_mesh_from_identifier(props.viseme_mesh)
|
||||
|
||||
if props.viseme_preview_mode:
|
||||
VisemePreview.end_preview(mesh)
|
||||
@@ -191,15 +193,17 @@ class AvatarToolkit_OT_CreateVisemes(Operator):
|
||||
return False
|
||||
|
||||
# Get mesh from UI selection
|
||||
from ..core.common import get_mesh_from_identifier
|
||||
props = context.scene.avatar_toolkit
|
||||
mesh_obj = bpy.data.objects.get(props.viseme_mesh)
|
||||
mesh_obj = get_mesh_from_identifier(props.viseme_mesh)
|
||||
|
||||
# Validate mesh
|
||||
return mesh_obj and mesh_obj.type == 'MESH'
|
||||
|
||||
def execute(self, context: Context) -> Set[str]:
|
||||
from ..core.common import get_mesh_from_identifier
|
||||
props = context.scene.avatar_toolkit
|
||||
mesh = bpy.data.objects.get(props.viseme_mesh) # Changed from context.active_object
|
||||
mesh = get_mesh_from_identifier(props.viseme_mesh)
|
||||
|
||||
if not mesh or not mesh.data.shape_keys:
|
||||
self.report({'ERROR'}, t("Visemes.error.no_shapekeys"))
|
||||
|
||||
+3
-2
@@ -34,8 +34,9 @@ class AvatarToolKit_PT_VisemesPanel(Panel):
|
||||
else:
|
||||
col.label(text=t("Visemes.no_armature"), icon='ERROR')
|
||||
|
||||
# Get selected mesh
|
||||
mesh_obj = bpy.data.objects.get(props.viseme_mesh)
|
||||
# Get selected mesh using safe identifier
|
||||
from ..core.common import get_mesh_from_identifier
|
||||
mesh_obj = get_mesh_from_identifier(props.viseme_mesh)
|
||||
if not mesh_obj or not mesh_obj.data or not mesh_obj.data.shape_keys:
|
||||
layout.label(text=t("Visemes.no_shapekeys"))
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user