Merge pull request #33 from 989onan/Import-Anything

Import anything
This commit is contained in:
Yusarina
2024-07-25 00:49:28 +01:00
committed by GitHub
8 changed files with 317 additions and 135 deletions
+15 -2
View File
@@ -1,6 +1,10 @@
import bpy import bpy
import numpy as np import numpy as np
from .dictionaries import bone_names from .dictionaries import bone_names
import threading
import time
import webbrowser
import typing
from typing import List, Optional, Tuple from typing import List, Optional, Tuple
from bpy.types import Object, ShapeKey, Mesh, Context from bpy.types import Object, ShapeKey, Mesh, Context
@@ -13,7 +17,6 @@ def clean_material_names(mesh: Mesh) -> None:
mesh.active_material_index = j mesh.active_material_index = j
mesh.active_material.name = mat.name[:-len(mat.name.rstrip('0')) - 1] mesh.active_material.name = mat.name[:-len(mat.name.rstrip('0')) - 1]
# This will fix faulty uv coordinates, cats did this a other way which can have unintended consequences, # This will fix faulty uv coordinates, cats did this a other way which can have unintended consequences,
# this is the best way i could of think of doing this for the time being, however may need improvements. # this is the best way i could of think of doing this for the time being, however may need improvements.
@@ -57,7 +60,7 @@ def get_armature(context: Context, armature_name: Optional[str] = None) -> Optio
if obj.type == "ARMATURE": if obj.type == "ARMATURE":
return obj return obj
return next((obj for obj in context.view_layer.objects if obj.type == 'ARMATURE'), None) return next((obj for obj in context.view_layer.objects if obj.type == 'ARMATURE'), None)
def get_armatures(self, context: Context) -> List[Tuple[str, str, str]]: def get_armatures(self, context: Context) -> List[Tuple[str, str, str]]:
return [(obj.name, obj.name, "") for obj in bpy.data.objects if obj.type == 'ARMATURE'] return [(obj.name, obj.name, "") for obj in bpy.data.objects if obj.type == 'ARMATURE']
@@ -92,3 +95,13 @@ def get_all_meshes(context: Context) -> List[Object]:
if armature and is_valid_armature(armature): if armature and is_valid_armature(armature):
return [obj for obj in bpy.data.objects if obj.type == 'MESH' and obj.parent == armature] return [obj for obj in bpy.data.objects if obj.type == 'MESH' and obj.parent == armature]
return [] return []
def open_web_after_delay_multi_threaded(delay: typing.Optional[float] = 1.0, url: typing.Union[str, typing.Any] = ""):
thread = threading.Thread(target=open_web_after_delay,args=[delay,url],name="open_browser_thread")
thread.start()
def open_web_after_delay(delay, url):
print("opening browser in "+str(delay)+" seconds.")
time.sleep(delay)
webbrowser.open_new_tab(url)
+52 -52
View File
@@ -5,85 +5,85 @@
# Note from @989onan: Please make sure to make your names are lowercase in this array. I banged my head metaphorically till I figured that out... # Note from @989onan: Please make sure to make your names are lowercase in this array. I banged my head metaphorically till I figured that out...
# Taken from Tuxedo/Cats # Taken from Tuxedo/Cats
bone_names = { bone_names = {
"right_shoulder": ["rightshoulder", "shoulderr", "rshoulder"], "right_shoulder": ["rightshoulder", "shoulderr", "rshoulder", "valvebipedbip01rclavicle"],
"right_arm": ["rightarm", "armr", "rarm", "upperarmr", "rupperarm", "rightupperarm", "upperarmright", "uparmr", "ruparm"], "right_arm": ["rightarm", "armr", "rarm", "upperarmr", "rupperarm", "rightupperarm", "uparmr", "ruparm", "valvebipedbip01rupperarm"],
"right_elbow": ["rightelbow", "elbowr", "relbow", "lowerarmr", "rightlowerarm", "lowerarmr","rlowerarm", "lowerarmright", "lowarmr", "rlowarm", "forearmr","rforearm"], "right_elbow": ["rightelbow", "elbowr", "relbow", "lowerarmr", "rightlowerarm", "lowerarmr","rlowerarm", "lowarmr", "rlowarm", "forearmr","rforearm", "valvebipedbip01rforearm"],
"right_wrist": ["rightwrist", "wristr", "rwrist", "handr", "righthand", "rhand"], "right_wrist": ["rightwrist", "wristr", "rwrist", "handr", "righthand", "rhand", "valvebipedbip01rhand"],
#hand l fingers #hand l fingers
"pinkie_0_r": ["littlefinger0r","pinkie0r","rpinkie0","pinkiemetacarpalr"], "pinkie_0_r": ["littlefinger0r","pinkie0r","rpinkie0","pinkiemetacarpalr"],
"pinkie_1_r": ["littlefinger1r","pinkie1r","rpinkie1","pinkieproximalr"], "pinkie_1_r": ["littlefinger1r","pinkie1r","rpinkie1","pinkieproximalr", "valvebipedbip01rfinger4"],
"pinkie_2_r": ["littlefinger2r","pinkie2r","rpinkie2","pinkieintermediater"], "pinkie_2_r": ["littlefinger2r","pinkie2r","rpinkie2","pinkieintermediater", "valvebipedbip01rfinger41"],
"pinkie_3_r": ["littlefinger3r","pinkie3r","rpinkie3","pinkiedistalr"], "pinkie_3_r": ["littlefinger3r","pinkie3r","rpinkie3","pinkiedistalr", "valvebipedbip01rfinger42"],
"ring_0_r": ["ringfinger0r","ring0r","rring0","ringmetacarpalr"], "ring_0_r": ["ringfinger0r","ring0r","rring0","ringmetacarpalr"],
"ring_1_r": ["ringfinger1r","ring1r","rring1","ringproximalr"], "ring_1_r": ["ringfinger1r","ring1r","rring1","ringproximalr", "valvebipedbip01rfinger3"],
"ring_2_r": ["ringfinger2r","ring2r","rring2","ringintermediater"], "ring_2_r": ["ringfinger2r","ring2r","rring2","ringintermediater", "valvebipedbip01rfinger31"],
"ring_3_r": ["ringfinger3r","ring3r","rring3","ringdistalr"], "ring_3_r": ["ringfinger3r","ring3r","rring3","ringdistalr", "valvebipedbip01rfinger32"],
"middle_0_r": ["middlefinger0r","middle0r","rmiddle0","middlemetacarpalr"], "middle_0_r": ["middlefinger0r","middle0r","rmiddle0","middlemetacarpalr"],
"middle_1_r": ["middlefinger1r","middle1r","rmiddle1","middleproximalr"], "middle_1_r": ["middlefinger1r","middle1r","rmiddle1","middleproximalr", "valvebipedbip01rfinger2"],
"middle_2_r": ["middlefinger2r","middle2r","rmiddle2","middleintermediater"], "middle_2_r": ["middlefinger2r","middle2r","rmiddle2","middleintermediater", "valvebipedbip01rfinger21"],
"middle_3_r": ["middlefinger3r","middle3r","rmiddle3","middledistalr"], "middle_3_r": ["middlefinger3r","middle3r","rmiddle3","middledistalr", "valvebipedbip01rfinger22"],
"index_0_r": ["indexfinger0r","index0r","rindex0","indexmetacarpalr"], "index_0_r": ["indexfinger0r","index0r","rindex0","indexmetacarpalr"],
"index_1_r": ["indexfinger1r","index1r","rindex1","indexproximalr"], "index_1_r": ["indexfinger1r","index1r","rindex1","indexproximalr", "valvebipedbip01rfinger1"],
"index_2_r": ["indexfinger2r","index2r","rindex2","indexintermediater"], "index_2_r": ["indexfinger2r","index2r","rindex2","indexintermediater", "valvebipedbip01rfinger11"],
"index_3_r": ["indexfinger3r","index3r","rindex3","indexdistalr"], "index_3_r": ["indexfinger3r","index3r","rindex3","indexdistalr", "valvebipedbip01rfinger12"],
"thumb_0_r": ["thumb0r","rthumb0","thumbmetacarpalr"], "thumb_0_r": ["thumb0r","rthumb0","thumbmetacarpalr"],
"thumb_1_r": ['thumb1r',"rthumb1","thumbproximalr"], "thumb_1_r": ['thumb1r',"rthumb1","thumbproximalr", "valvebipedbip01rfinger0"],
"thumb_2_r": ['thumb2r',"rthumb2","thumbintermediater"], "thumb_2_r": ['thumb2r',"rthumb2","thumbintermediater", "valvebipedbip01rfinger01"],
"thumb_3_r": ['thumb3r',"rthumb3","thumbdistalr"], "thumb_3_r": ['thumb3r',"rthumb3","thumbdistalr", "valvebipedbip01rfinger02"],
"right_leg": ["rightleg", "legr", "rleg", "upperlegr", "rupperleg", "thighr", "rightupperleg", "upperlegright", "uplegr", "rupleg"], "right_leg": ["rightleg", "legr", "rleg", "upperlegr", "rupperleg", "thighr", "rightupperleg", "uplegr", "rupleg", "valvebipedbip01rthigh"],
"right_knee": ["rightknee", "kneer", "rknee", "lowerlegr", "calfr", "rlowerleg", "rcalf", "rightlowerleg", "lowerlegright", "lowlegr", "rlowleg"], "right_knee": ["rightknee", "kneer", "rknee", "lowerlegr", "calfr", "rlowerleg", "rcalf", "rightlowerleg", "lowlegr", "rlowleg", "valvebipedbip01rcalf"],
"right_ankle": ["rightankle", "ankler", "rankle", "footright", "footr", "rfoot", "rightfoot", "rightfeet", "feetright", "rfeet", "feetr"], "right_ankle": ["rightankle", "ankler", "rankle", "rightfoot", "footr", "rfoot", "rightfoot", "rightfeet", "feetright", "rfeet", "feetr", "valvebipedbip01rfoot"],
"right_toe": ["righttoe", "toeright", "toer", "rtoe", "toesr", "rtoes"], "right_toe": ["righttoe", "toeright", "toer", "rtoe", "toesr", "rtoes", "valvebipedbip01rtoe0"],
"left_shoulder": ["leftshoulder", "shoulderl", "lshoulder"], "left_shoulder": ["leftshoulder", "shoulderl", "lshoulder", "valvebipedbip01lclavicle"],
"left_arm": ["leftarm", "arml", "rarm", "upperarml", "lupperarm", "leftupperarm", "upperarmleft", "uparml", "luparm"], "left_arm": ["leftarm", "arml", "rarm", "upperarml", "lupperarm", "leftupperarm", "uparml", "luparm", "valvebipedbip01lupperarm"],
"left_elbow": ["leftelbow", "elbowl", "lelbow", "lowerarml", "leftlowerarm", "lowerarmleft", "lowerarml", "llowerarm", "lowarml", "llowarm", "forearml","lforearm"], "left_elbow": ["leftelbow", "elbowl", "lelbow", "lowerarml", "leftlowerarm", "lowerarml", "llowerarm", "lowarml", "llowarm", "forearml","lforearm", "valvebipedbip01lforearm"],
"left_wrist": ["leftwrist", "wristl", "lwrist", "handl", "lefthand", "lhand"], "left_wrist": ["leftwrist", "wristl", "lwrist", "handl", "lefthand", "lhand", "valvebipedbip01lhand"],
#hand l fingers #hand l fingers
"pinkie_0_l": ["pinkiefinger0l","pinkie0l","lpinkie0","pinkiemetacarpall"], "pinkie_0_l": ["pinkiefinger0l","pinkie0l","lpinkie0","pinkiemetacarpall"],
"pinkie_1_l": ["littlefinger1l","pinkie1l","lpinkie1","pinkieproximall"], "pinkie_1_l": ["littlefinger1l","pinkie1l","lpinkie1","pinkieproximall", "valvebipedbip01lfinger4"],
"pinkie_2_l": ["littlefinger2l","pinkie2l","lpinkie2","pinkieintermediatel"], "pinkie_2_l": ["littlefinger2l","pinkie2l","lpinkie2","pinkieintermediatel", "valvebipedbip01lfinger41"],
"pinkie_3_l": ["littlefinger3l","pinkie3l","lpinkie3","pinkiedistall"], "pinkie_3_l": ["littlefinger3l","pinkie3l","lpinkie3","pinkiedistall", "valvebipedbip01lfinger42"],
"ring_0_l": ["ringfinger0l","ring0l","lring0","ringmetacarpall"], "ring_0_l": ["ringfinger0l","ring0l","lring0","ringmetacarpall"],
"ring_1_l": ["ringfinger1l","ring1l","lring1","ringproximall"], "ring_1_l": ["ringfinger1l","ring1l","lring1","ringproximall", "valvebipedbip01lfinger3"],
"ring_2_l": ["ringfinger2l","ring2l","lring2","ringintermediatel"], "ring_2_l": ["ringfinger2l","ring2l","lring2","ringintermediatel", "valvebipedbip01lfinger31"],
"ring_3_l": ["ringfinger3l","ring3l","lring3","ringdistall"], "ring_3_l": ["ringfinger3l","ring3l","lring3","ringdistall", "valvebipedbip01lfinger32"],
"middle_0_l": ["middlefinger0l","middle_0l","lmiddle0","middlemetacarpall"], "middle_0_l": ["middlefinger0l","middle_0l","lmiddle0","middlemetacarpall"],
"middle_1_l": ["middlefinger1l","middle_1l","lmiddle1","middleproximall"], "middle_1_l": ["middlefinger1l","middle_1l","lmiddle1","middleproximall", "valvebipedbip01lfinger2"],
"middle_2_l": ["middlefinger2l","middle_2l","lmiddle2","middleintermediatel"], "middle_2_l": ["middlefinger2l","middle_2l","lmiddle2","middleintermediatel", "valvebipedbip01lfinger21"],
"middle_3_l": ["middlefinger3l","middle_3l","lmiddle3","middledistall"], "middle_3_l": ["middlefinger3l","middle_3l","lmiddle3","middledistall", "valvebipedbip01lfinger22"],
"index_0_l": ["indexfinger0l","index0l","lindex0","indexmetacarpall"], "index_0_l": ["indexfinger0l","index0l","lindex0","indexmetacarpall"],
"index_1_l": ["indexfinger1l","index1l","lindex1","indexproximall"], "index_1_l": ["indexfinger1l","index1l","lindex1","indexproximall", "valvebipedbip01lfinger1"],
"index_2_l": ["indexfinger2l","index2l","lindex2","indexintermediatel"], "index_2_l": ["indexfinger2l","index2l","lindex2","indexintermediatel", "valvebipedbip01lfinger11"],
"index_3_l": ["indexfinger3l","index3l","lindex3","indexdistall"], "index_3_l": ["indexfinger3l","index3l","lindex3","indexdistall", "valvebipedbip01lfinger12"],
"thumb_0_l": ["thumb0l","lthumb0","thumbmetacarpall"], "thumb_0_l": ["thumb0l","lthumb0","thumbmetacarpall"],
"thumb_1_l": ['thumb1l',"lthumb1","thumbproximall"], "thumb_1_l": ['thumb1l',"lthumb1","thumbproximall", "valvebipedbip01lfinger0"],
"thumb_2_l": ['thumb2l',"lthumb2","thumbintermediatel"], "thumb_2_l": ['thumb2l',"lthumb2","thumbintermediatel", "valvebipedbip01lfinger01"],
"thumb_3_l": ['thumb3l',"lthumb3","thumbdistall"], "thumb_3_l": ['thumb3l',"lthumb3","thumbdistall", "valvebipedbip01lfinger02"],
"left_leg": ["leftleg", "legl", "lleg", "upperlegl", "lupperleg", "thighl", "leftupperleg", "upperlegleft", "uplegl", "lupleg"], "left_leg": ["leftleg", "legl", "lleg", "upperlegl", "lupperleg", "thighl", "leftupperleg", "uplegl", "lupleg", "valvebipedbip01lthigh"],
"left_knee": ["leftknee", "kneel", "lknee", "lowerlegl", "llowerleg", "calfl", "lcalf", "leftlowerleg", "lowerlegleft", 'lowlegl', 'llowleg'], "left_knee": ["leftknee", "kneel", "lknee", "lowerlegl", "llowerleg", "calfl", "lcalf", "leftlowerleg", 'lowlegl', 'llowleg', "valvebipedbip01lcalf"],
"left_ankle": ["leftankle", "anklel", "rankle", "footleft", "footl", "lfoot", "leftfoot", "leftfeet", "feetleft", "lfeet", "feetl"], "left_ankle": ["leftankle", "anklel", "rankle", "leftfoot", "footl", "lfoot", "leftfoot", "leftfeet", "feetleft", "lfeet", "feetl", "valvebipedbip01lfoot"],
"left_toe": ["lefttoe", "toeleft", "toel", "ltoe", "toesl", "ltoes"], "left_toe": ["lefttoe", "toeleft", "toel", "ltoe", "toesl", "ltoes", "valvebipedbip01ltoe0"],
"hips": ["pelvis", "hips", "hip"], "hips": ["pelvis", "hips", "valvebipedbip01pelvis"],
"spine": ["torso", "spine"], "spine": ["torso", "spine", "valvebipedbip01spine"],
"chest": ["chest"], "chest": ["chest", "valvebipedbip01spine1"],
"upper_chest": ["upperchest", "chestupper"], "upper_chest": ["upperchest", "valvebipedbip01spine4"],
"neck": ["neck"], "neck": ["neck", "valvebipedbip01neck1"],
"head": ["head", "cabeza"], "head": ["head", "valvebipedbip01head1"],
"left_eye": ["eyeleft", "lefteye", "eyel", "leye"], "left_eye": ["eyeleft", "lefteye", "eyel", "leye"],
"right_eye": ["eyeright", "righteye", "eyer", "reye"], "right_eye": ["eyeright", "righteye", "eyer", "reye"],
} }
+48 -12
View File
@@ -1,17 +1,53 @@
import bpy import bpy
# Importers which don't need much code should be added here, however if a importer needs alot of code # 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. # 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
# FBX Importer settings borrowed form Cat's Blender Plugin import importlib.util
def import_fbx(filepath): import os
try: import typing
bpy.ops.import_scene.fbx( from .import_pmx import import_pmx
filepath=filepath, from .import_pmd import import_pmd
automatic_bone_orientation=False,
use_prepost_rot=False, if importlib.util.find_spec("io_scene_valvesource") is not None:
use_anim=False #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
except (TypeError, ValueError) as e:
print(f"Error importing FBX: {str(e)}") 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)))),
"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
imports = concat_imports_filter(import_types)
+3
View File
@@ -0,0 +1,3 @@
{
"language": 0
}
+1 -1
View File
@@ -49,7 +49,7 @@ def toposort(deps_dict):
unsorted.append(value) unsorted.append(value)
deps_dict = {value : deps_dict[value] - sorted_values for value in unsorted} deps_dict = {value : deps_dict[value] - sorted_values for value in unsorted}
sort_order(sorted_list) #to sort by 'bl_order' so we can choose how things may appear in the ui #sort_order(sorted_list) #to sort by 'bl_order' so we can choose how things may appear in the ui
return sorted_list return sorted_list
+186
View File
@@ -0,0 +1,186 @@
import bpy
from bpy.types import Operator
from bpy_extras.io_utils import ImportHelper
from ..core.register import register_wrap
from ..core.importer import imports, import_types
from ..functions.translations import t
import pathlib
import os
from ..core import common
@register_wrap
class ImportAnyModel(Operator, ImportHelper):
bl_idname = 'avatar_toolkit.import_any_model'
bl_label = t('Tools.import_any_model.label')
bl_description = t('Tools.import_any_model.desc')
bl_options = {'REGISTER', 'UNDO'}
files: bpy.props.CollectionProperty(type=bpy.types.OperatorFileListElement, options={'HIDDEN', 'SKIP_SAVE'})
filter_glob: bpy.props.StringProperty(default = imports, options={'HIDDEN','SKIP_SAVE'})
directory: bpy.props.StringProperty(maxlen=1024, subtype='FILE_PATH', options={'HIDDEN', 'SKIP_SAVE'})
#since I wrote this myself, a bit more efficent than cats. mostly - @989onan
def execute(self, context: bpy.types.Context):
file_grouping_dict: dict[str, list[dict[str,str]]] = dict()#group our files so our importers can import them together. in the case of OBJ+MTL and others that need grouped files, this is extremely important.
#check if we are importing multiple files
is_multi = False
try:
for file in self.files:
pass
is_multi = True
except Exception as e:
is_multi = False
print(e)
#put the files together into lists of same importers
if(is_multi):
for file in self.files:
fullpath = os.path.join(self.directory,os.path.basename(file.name))
name = pathlib.Path(fullpath).suffix.replace(".","")
#this makes sure our imports that should be grouped stay together.
#basically the method checks for if the first value has a lambda with the same bytecode as another lambda, then it will use that value's key (ex:"obj"<->"mtl" or "fbx"), keeping same importers together
try:
name2 = next(key for key,value in import_types.items() if value.__code__.co_code == import_types[name].__code__.co_code)
print(name +" is the same importer as "+name2+", grouping.")
name = name2
except Exception as e:
print("error when trying to find a value of the same value in the kinds of importers. May just be an import type that's a singlet:")
print(e)
if name not in file_grouping_dict: file_grouping_dict[name] = []
file_grouping_dict[name].append({"name": os.path.basename(file.name)}) #emulate passing a list of files.
else:
fullpath: str = os.path.join(os.path.dirname(self.filepath),os.path.basename(self.filepath))
name = pathlib.Path(fullpath).suffix.replace(".","")
if name not in file_grouping_dict: file_grouping_dict[name] = []
file_grouping_dict[name].append({"name": fullpath}) #emulate passing a list of files.
#import the files together to make sure things like obj import together. This is important
for file_group_name,files in file_grouping_dict.items():
try:
if(self.directory):
print(files)
import_types[file_group_name](self.directory,files,self.filepath)
else:
import_types[file_group_name]("",files,self.filepath) #give an empty directory, works just fine for 90%
except AttributeError as e:
print("Warning, you may not have the required importer!")
common.open_web_after_delay_multi_threaded(delay=12, url=t('Importing.importer_search_term').format(extension = file_group_name))
self.report({'ERROR'},t('Importing.need_importer').format(extension = file_group_name))
print("importer error was:")
print(e)
return {'FINISHED'}
#This needs to be done with our own MMD importer:
"""
#stolen from cats. Oh wait I made this code riiiiiiight - @989onan
@register_wrap
class ImportMMDAnimation(bpy.types.Operator, ImportHelper):
bl_idname = 'avatar_toolkit.import_mmd_animation'
bl_label = t('Importer.mmd_anim_importer.label')
bl_description = t('Importer.mmd_anim_importer.desc')
bl_options = {'INTERNAL', 'UNDO'}
filter_glob: bpy.props.StringProperty(
default="*.vmd",
options={'HIDDEN'}
)
files: bpy.props.CollectionProperty(type=bpy.types.OperatorFileListElement, options={'HIDDEN', 'SKIP_SAVE'})
directory: bpy.props.StringProperty(maxlen=1024, subtype='FILE_PATH', options={'HIDDEN', 'SKIP_SAVE'})
filepath: bpy.props.StringProperty()
@classmethod
def poll(cls, context):
if common.get_armature(context) is None:
return False
return True
def execute(self, context):
# Make sure that the first layer is visible
if hasattr(context.scene, 'layers'):
context.scene.layers[0] = True
filename, extension = os.path.splitext(self.filepath)
if(extension == ".vmd"):
#A dictionary to change the current model to MMD importer compatable temporarily
bonedict = {
"chest":"UpperBody",
"neck":"Neck",
"head":"Head",
"hips":"Center",
"spine":"LowerBody",
"right_wrist":"Wrist_R",
"right_elbow":"Elbow_R",
"right_arm":"Arm_R",
"right_shoulder":"Shoulder_R",
"right_leg":"Leg_R",
"right_knee":"Knee_R",
"right_ankle":"Ankle_R",
"right_toe":"Toe_R",
"left_wrist":"Wrist_L",
"left_elbow":"Elbow_L",
"left_arm":"Arm_L",
"left_shoulder":"Shoulder_L",
"left_leg":"Leg_L",
"left_knee":"Knee_L",
"left_ankle":"Ankle_L",
"left_toe":"Toe_L"
}
armature = common.get_armature(context)
common.unselect_all()
common.Set_Mode(context, 'OBJECT')
common.unselect_all()
common.set_active(armature)
orig_names = dict()
reverse_bone_lookup = dict()
for (preferred_name, name_list) in bone_names.items():
for name in name_list:
reverse_bone_lookup[name] = preferred_name
for bone in armature.data.bones:
if common.simplify_bonename(bone.name) in reverse_bone_lookup and reverse_bone_lookup[common.simplify_bonename(bone.name)] in bonedict:
orig_names[bonedict[reverse_bone_lookup[common.simplify_bonename(bone.name)]]] = bone.name
bone.name = bonedict[reverse_bone_lookup[common.simplify_bonename(bone.name)]]
try:
bpy.ops.mmd_tools.import_vmd(filepath=self.filepath,bone_mapper='RENAMED_BONES',use_underscore=True, dictionary='INTERNAL')
except AttributeError as e:
print("importer error was:")
print(e)
print(t('Importing.importer_search_term'))
common.open_web_after_delay_multi_threaded(delay=12, url=t('Importing.importer_search_term').format(extension = "MMD"))
self.report({'ERROR'},t('Importing.need_importer').format(extension = "MMD"))
return {'CANCELLED'}
#iterate through bones and put them back, therefore blender API will change the animation to be correct.
#this is because renaming bones fixes the animation targets in the data model.
for bone in armature.data.bones:
if common.simplify_bonename(bone.name) in orig_names:
bone.name = orig_names[common.simplify_bonename(bone.name)]
common.unselect_all()
common.Set_Mode(context, 'OBJECT')
common.unselect_all()
common.set_active(armature)
return {'FINISHED'} """
+10 -1
View File
@@ -36,7 +36,16 @@
"Settings.translation_restart_popup.label": "Translation Update", "Settings.translation_restart_popup.label": "Translation Update",
"Settings.translation_restart_popup.description": "Information about translation updates", "Settings.translation_restart_popup.description": "Information about translation updates",
"Settings.translation_restart_popup.message1": "Some translations may not apply", "Settings.translation_restart_popup.message1": "Some translations may not apply",
"Settings.translation_restart_popup.message2": "until you restart Blender." "Settings.translation_restart_popup.message2": "until you restart Blender.",
"Importing.need_importer":"You do not have the required importer for the {extension} type! Opening web browser for importer search term...",
"Importer.mmd_anim_importer.label":"MMD Animation",
"Importer.mmd_anim_importer.desc":"Import a MMD Animation (.vmd)",
"Importing.importer_search_term":"https://search.brave.com/search?q=blender+{extension}+importer+addon&source=web",
"Importer.export_resonite.label":"Export to Resonite",
"Importer.export_resonite.desc":"Export to Resonite as a GLTF. Make sure your model is to scale in blender, and import as meters in Resonite.",
"Importer.export_vrchat.label":"Export to VRChat",
"Importer.export_vrchat.desc":"Export to VRChat, may also work for ChilloutVR. Is similar to Cats export."
} }
} }
+2 -67
View File
@@ -6,7 +6,7 @@ from ..functions.translations import t
from ..core.import_pmx import import_pmx from ..core.import_pmx import import_pmx
from ..core.import_pmd import import_pmd from ..core.import_pmd import import_pmd
from ..core.importer import import_fbx from ..functions.import_anything import ImportAnyModel
from ..core.common import get_selected_armature, set_selected_armature from ..core.common import get_selected_armature, set_selected_armature
@register_wrap @register_wrap
@@ -31,29 +31,9 @@ class AvatarToolkitQuickAccessPanel(bpy.types.Panel):
row = layout.row(align=True) row = layout.row(align=True)
row.scale_y = 1.5 row.scale_y = 1.5
row.operator("avatar_toolkit.import_menu", text=t("Quick_Access.import")) row.operator(ImportAnyModel.bl_idname, text=t("Quick_Access.import"))
row.operator("avatar_toolkit.export_menu", text=t("Quick_Access.export")) row.operator("avatar_toolkit.export_menu", text=t("Quick_Access.export"))
@register_wrap
class AVATAR_TOOLKIT_OT_import_menu(bpy.types.Operator):
bl_idname = "avatar_toolkit.import_menu"
bl_label = t("Quick_Access.import_menu.label")
bl_description = t("Quick_Access.import_menu.desc")
def execute(self, context: Context):
return {'FINISHED'}
def invoke(self, context: Context, event):
wm = context.window_manager
return wm.invoke_popup(self, width=200)
def draw(self, context: Context):
layout = self.layout
layout.label(text="Select Import Method")
layout.operator("avatar_toolkit.import_pmx", text=t("Quick_Access.import_pmx"))
layout.operator("avatar_toolkit.import_pmd", text=t("Quick_Access.import_pmd"))
layout.operator("avatar_toolkit.import_fbx", text="Import FBX")
@register_wrap @register_wrap
class AVATAR_TOOLKIT_OT_export_menu(bpy.types.Operator): class AVATAR_TOOLKIT_OT_export_menu(bpy.types.Operator):
bl_idname = "avatar_toolkit.export_menu" bl_idname = "avatar_toolkit.export_menu"
@@ -77,51 +57,6 @@ class AVATAR_TOOLKIT_OT_export_menu(bpy.types.Operator):
layout.operator("avatar_toolkit.export_resonite", text=t("Quick_Access.select_export_resonite.label")) layout.operator("avatar_toolkit.export_resonite", text=t("Quick_Access.select_export_resonite.label"))
layout.operator("avatar_toolkit.export_fbx", text="Export FBX") layout.operator("avatar_toolkit.export_fbx", text="Export FBX")
@register_wrap
class AVATAR_TOOLKIT_OT_import_pmx(bpy.types.Operator):
bl_idname = "avatar_toolkit.import_pmx"
bl_label = t("Quick_Access.import_pmx")
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context: Context):
import_pmx(self.filepath)
return {'FINISHED'}
def invoke(self, context: Context, event):
context.window_manager.fileselect_add(self)
return {'RUNNING_MODAL'}
@register_wrap
class AVATAR_TOOLKIT_OT_import_pmd(bpy.types.Operator):
bl_idname = "avatar_toolkit.import_pmd"
bl_label = t("Quick_Access.import_pmd")
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context: Context):
import_pmd(self.filepath)
return {'FINISHED'}
def invoke(self, context: Context, event):
context.window_manager.fileselect_add(self)
return {'RUNNING_MODAL'}
@register_wrap
class AVATAR_TOOLKIT_OT_import_fbx(bpy.types.Operator):
bl_idname = "avatar_toolkit.import_fbx"
bl_label = "Import FBX"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
import_fbx(self.filepath)
return {'FINISHED'}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {'RUNNING_MODAL'}
@register_wrap @register_wrap
class AVATAR_TOOLKIT_OT_export_fbx(bpy.types.Operator): class AVATAR_TOOLKIT_OT_export_fbx(bpy.types.Operator):
bl_idname = 'avatar_toolkit.export_fbx' bl_idname = 'avatar_toolkit.export_fbx'