Start of the Major Overhaul

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.
This commit is contained in:
Yusarina
2024-12-03 22:58:17 +00:00
parent 7f9dc20564
commit ff23d23cfc
38 changed files with 604 additions and 4765 deletions
+119 -44
View File
@@ -1,54 +1,129 @@
import bpy
# Importers which don't need much code should be added here, however if a importer needs alot of code
# Like the PMX and PMD importers, they should be added to their own files and referenced in the import_types str->lambda dictionary.
#See below comments on how the system works. - @989onan
import importlib.util
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
if importlib.util.find_spec("io_scene_valvesource") is not None:
#from .....scripts.addons.io_scene_valvesource.import_smd import SmdImporter #<- use this to check if your IDE is working properly. idfk
from io_scene_valvesource.import_smd import SmdImporter #ignore IDE bitching this is fine, trust me, also above comment should be okay to an IDE usually if set up right. ^_^ - @989onan
# Configure logging
logging.basicConfig(level=logging.INFO)
logger: logging.Logger = logging.getLogger(__name__)
def import_multi_files(method = None, directory: typing.Optional[str] = None, files: list[dict[str,str]] = None, filepath: typing.Optional[str] = ""):
if not files:
method(directory, filepath)
else:
for file in files:
fullpath = os.path.join(directory,os.path.basename(file["name"]))
print("run method!")
method(directory, fullpath)
#each import should map to a type. even in the case that multiple methods should import together, or have the same import method. Make sure the lambdas match so they get grouped together
#In the case of a file importer that takes only one file argument and each one needs individual import, use above method. (example of it in use is ".dae" format)
import_types: dict[str, typing.Callable[[str, list[dict[str,str]], str], None]] = {
"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)),
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):
names = ""
for importer in imports.keys():
names = names+"*."+importer+";"
return names
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 = concat_imports_filter(import_types)
imports: str = concat_imports_filter(import_types)