- 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
88 lines
3.1 KiB
Python
88 lines
3.1 KiB
Python
# SPDX-License-Identifier: MIT OR GPL-3.0-or-later
|
|
import math
|
|
from collections.abc import Iterator
|
|
from contextlib import contextmanager
|
|
from typing import Final, Optional
|
|
from uuid import uuid4
|
|
|
|
import bpy
|
|
from bpy.types import Context
|
|
|
|
from .logger import get_logger
|
|
|
|
logger = get_logger(__name__)
|
|
|
|
|
|
class PartialProgress:
|
|
def __init__(
|
|
self, progress: "Progress", partial_start_ratio: float, partial_end_ratio: float
|
|
) -> None:
|
|
self.progress: Final = progress
|
|
self.partial_start_ratio: Final = partial_start_ratio
|
|
self.partial_end_ratio: Final = partial_end_ratio
|
|
|
|
def update(self, ratio: float) -> None:
|
|
ratio = min(max(0.0, ratio), 1.0)
|
|
self.progress.update(
|
|
self.partial_start_ratio
|
|
+ ratio * (self.partial_end_ratio - self.partial_start_ratio)
|
|
)
|
|
|
|
|
|
class Progress:
|
|
active_progress_uuid: Optional[str] = None
|
|
|
|
def __init__(self, context: Context, *, show_progress: bool) -> None:
|
|
self.context: Final = context
|
|
self.show_progress: Final = show_progress
|
|
self.uuid: Final = uuid4().hex
|
|
self.last_ratio = 0.0
|
|
|
|
def partial_progress(self, partial_end_ratio: float) -> PartialProgress:
|
|
partial_end_ratio = min(max(0.0, partial_end_ratio), 1.0)
|
|
return PartialProgress(self, self.last_ratio, partial_end_ratio)
|
|
|
|
def update(self, ratio: float) -> None:
|
|
ratio = min(max(0.0, ratio), 1.0)
|
|
self.last_ratio = ratio
|
|
if self.active_progress_uuid != self.uuid:
|
|
logger.error(
|
|
"Progress.update() called from different progress active=%s self=%s",
|
|
self.active_progress_uuid,
|
|
self.uuid,
|
|
)
|
|
return
|
|
|
|
if not self.show_progress:
|
|
return
|
|
|
|
if bpy.app.version >= (5, 0):
|
|
progress_value = ratio * 9999
|
|
else:
|
|
# The mouse cursor becomes a four-digit number, and there is an area
|
|
# where values from 0 to 9999 can be displayed. However, if the
|
|
# progress rate is directly converted to a number from 0 to 9999, the
|
|
# last two digits will frequently round-trip, making the progress
|
|
# difficult to understand. Therefore, only the last two digits display
|
|
# area is used to show the progress rate as a number from 0 to 99.
|
|
progress_value = math.floor(ratio * 99)
|
|
|
|
self.context.window_manager.progress_update(progress_value)
|
|
|
|
|
|
@contextmanager
|
|
def create_progress(
|
|
context: Context, *, show_progress: bool = True
|
|
) -> Iterator[Progress]:
|
|
saved_progress_uuid = Progress.active_progress_uuid
|
|
try:
|
|
if show_progress and Progress.active_progress_uuid is None:
|
|
context.window_manager.progress_begin(0, 9999)
|
|
progress = Progress(context, show_progress=show_progress)
|
|
Progress.active_progress_uuid = progress.uuid
|
|
yield progress
|
|
finally:
|
|
Progress.active_progress_uuid = saved_progress_uuid
|
|
if show_progress and Progress.active_progress_uuid is None:
|
|
context.window_manager.progress_end()
|