ff23d23cfc
I decided to go through each function and UI section one by one, improving and overhauling things. Each function and section is going to be fully tested and not rushed out. This is the best way to catch things, but also include the code base as much as possible.
130 lines
5.4 KiB
Python
130 lines
5.4 KiB
Python
import bpy
|
|
import logging
|
|
import os
|
|
import typing
|
|
from typing import Optional, Callable, Dict, List, Union, Set
|
|
from ..common import clear_default_objects
|
|
from .import_pmx import import_pmx
|
|
from .import_pmd import import_pmd
|
|
|
|
# Configure logging
|
|
logging.basicConfig(level=logging.INFO)
|
|
logger: logging.Logger = logging.getLogger(__name__)
|
|
|
|
import importlib.util
|
|
|
|
if importlib.util.find_spec("io_scene_valvesource") is not None:
|
|
from io_scene_valvesource.import_smd import SmdImporter
|
|
|
|
class ImportProgress:
|
|
"""Tracks and logs the progress of multi-file imports"""
|
|
def __init__(self, total_files: int):
|
|
self.total: int = total_files
|
|
self.current: int = 0
|
|
|
|
def update(self, filename: str) -> None:
|
|
"""Update import progress and log current file"""
|
|
self.current += 1
|
|
logger.info(f"Importing {filename} ({self.current}/{self.total})")
|
|
|
|
def validate_file(filepath: str) -> bool:
|
|
"""
|
|
Validate if a file exists and is accessible
|
|
Returns: True if file is valid, False otherwise
|
|
"""
|
|
if not os.path.exists(filepath):
|
|
logger.error(f"File not found: {filepath}")
|
|
return False
|
|
if not os.path.isfile(filepath):
|
|
logger.error(f"Not a file: {filepath}")
|
|
return False
|
|
return True
|
|
|
|
def import_multi_files(
|
|
method: Optional[Callable] = None,
|
|
directory: Optional[str] = None,
|
|
files: Optional[List[Dict[str, str]]] = None,
|
|
filepath: str = "",
|
|
progress_callback: Optional[Callable[[str], None]] = None
|
|
) -> None:
|
|
"""
|
|
Import multiple files using the specified import method
|
|
|
|
Args:
|
|
method: Import method to use
|
|
directory: Directory containing files
|
|
files: List of files to import
|
|
filepath: Single file path to import
|
|
progress_callback: Callback for progress updates
|
|
"""
|
|
try:
|
|
if not method:
|
|
raise ValueError("Import method not specified")
|
|
|
|
if not files:
|
|
if not validate_file(filepath):
|
|
return
|
|
method(directory, filepath)
|
|
if progress_callback:
|
|
progress_callback(filepath)
|
|
else:
|
|
progress = ImportProgress(len(files))
|
|
for file in files:
|
|
fullpath: str = os.path.join(directory, os.path.basename(file["name"]))
|
|
if not validate_file(fullpath):
|
|
continue
|
|
|
|
logger.info(f"Importing file: {fullpath}")
|
|
method(directory, fullpath)
|
|
|
|
if progress_callback:
|
|
progress_callback(fullpath)
|
|
progress.update(file["name"])
|
|
|
|
except Exception as e:
|
|
logger.error(f"Import failed: {str(e)}", exc_info=True)
|
|
raise
|
|
|
|
ImportMethod = Callable[[str, List[Dict[str, str]], str], None]
|
|
|
|
import_types: Dict[str, ImportMethod] = {
|
|
"fbx": lambda directory, files, filepath: bpy.ops.import_scene.fbx(
|
|
files=files, directory=directory, filepath=filepath,
|
|
automatic_bone_orientation=False, use_prepost_rot=False, use_anim=False
|
|
),
|
|
"smd": lambda directory, files, filepath: eval("bpy."+SmdImporter.bl_idname+".(files=files, directory=directory, filepath=filepath)"),
|
|
"dmx": lambda directory, files, filepath: eval("bpy."+SmdImporter.bl_idname+".(files=files, directory=directory, filepath=filepath)"),
|
|
"gltf": lambda directory, files, filepath: bpy.ops.import_scene.gltf(files=files, filepath=filepath),
|
|
"glb": lambda directory, files, filepath: bpy.ops.import_scene.gltf(files=files, filepath=filepath),
|
|
"qc": lambda directory, files, filepath: eval("bpy."+SmdImporter.bl_idname+".(files=files, directory=directory, filepath=filepath)"),
|
|
"obj": lambda directory, files, filepath: bpy.ops.wm.obj_import(files=files, directory=directory, filepath=filepath),
|
|
"dae": lambda directory, files, filepath: import_multi_files(
|
|
directory=directory,
|
|
files=files,
|
|
filepath=filepath,
|
|
method=lambda directory, filepath: bpy.ops.wm.collada_import(
|
|
filepath=filepath, auto_connect=True, find_chains=True, fix_orientation=True
|
|
)
|
|
),
|
|
"3ds": lambda directory, files, filepath: bpy.ops.import_scene.max3ds(files=files, directory=directory, filepath=filepath),
|
|
"stl": lambda directory, files, filepath: bpy.ops.import_mesh.stl(files=files, directory=directory, filepath=filepath),
|
|
"mtl": lambda directory, files, filepath: bpy.ops.wm.obj_import(files=files, directory=directory, filepath=filepath),
|
|
"x3d": lambda directory, files, filepath: bpy.ops.import_scene.x3d(files=files, directory=directory, filepath=filepath),
|
|
"wrl": lambda directory, files, filepath: bpy.ops.import_scene.x3d(files=files, directory=directory, filepath=filepath),
|
|
"vmd": lambda directory, files, filepath: import_multi_files(
|
|
directory=directory,
|
|
files=files,
|
|
filepath=filepath,
|
|
method=lambda directory, filepath: bpy.ops.tuxedo.import_mmd_animation(directory=directory, filepath=filepath)
|
|
),
|
|
"vrm": lambda directory, files, filepath: bpy.ops.import_scene.vrm(filepath=filepath),
|
|
"pmx": lambda directory, files, filepath: import_pmx(filepath),
|
|
"pmd": lambda directory, files, filepath: import_pmd(filepath),
|
|
}
|
|
|
|
def concat_imports_filter(imports: Dict[str, ImportMethod]) -> str:
|
|
"""Create a file filter string from import types"""
|
|
return "".join(f"*.{importer};" for importer in imports.keys())
|
|
|
|
imports: str = concat_imports_filter(import_types)
|