From 02272ed2195381d2d6d16ba1e4a3795ced5b57de Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Fri, 25 Sep 2020 08:56:57 +0900 Subject: [PATCH 01/37] =?UTF-8?q?cython=E5=86=8D=E6=8C=91=E6=88=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 6 ++ .vscode/launch.json | 15 ++-- command.txt | 156 ++++++++++++++++++++++++++++++++++++++++++ gui.bat | 2 +- pyinstaller64.bat | 4 +- smooth.bat | 25 +++++++ src/setup.bat | 13 ++++ src/setup.py | 14 ++++ src/setup_ext.py | 53 ++++++++++++++ src/setup_install.bat | 10 +++ src/setup_install.py | 9 +++ vmdising_np64.spec | 8 +-- 12 files changed, 300 insertions(+), 15 deletions(-) create mode 100644 smooth.bat create mode 100644 src/setup.bat create mode 100644 src/setup.py create mode 100644 src/setup_ext.py create mode 100644 src/setup_install.bat create mode 100644 src/setup_install.py diff --git a/.gitignore b/.gitignore index 8f19837..7b7d148 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,10 @@ build dist *.zip *.log +*.c +*.pyd +*.prof +*.lprof src/model_all_test.py VmdSizingSelfAll2.bat rivision.txt @@ -51,3 +55,5 @@ src/VmdSizingForm3.py.bak main.prof executor.py.lprof *.thread +src/MotionSupporter.ico +*.html diff --git a/.vscode/launch.json b/.vscode/launch.json index c4f6567..ed9504f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,11 +1,8 @@ { - // IntelliSense を使用して利用可能な属性を学べます。 - // 既存の属性の説明をホバーして表示します。 - // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { - "name": "Python: debug_exec", + "name": "exec_sizing", "type": "python", "request": "launch", "program": "${workspaceFolder}/src/executor.py", @@ -164,7 +161,7 @@ } }, { - "name": "Python: debug_exec_smooth", + "name": "exec_smooth", "type": "python", "request": "launch", "program": "${workspaceFolder}/src/executor_smooth.py", @@ -201,7 +198,7 @@ } }, { - "name": "Python: debug_exec_brend", + "name": "exec_brend", "type": "python", "request": "launch", "program": "${workspaceFolder}/src/blend_pmx.py", @@ -217,7 +214,7 @@ ] }, { - "name": "Python: debug_exec_gui", + "name": "exec_gui", "type": "python", "request": "launch", "program": "${workspaceFolder}/src/executor.py", @@ -230,7 +227,7 @@ ] }, { - "name": "Python: debug_exec_thread", + "name": "exec_thread", "type": "python", "request": "launch", "program": "${workspaceFolder}/src/thread.py", @@ -243,7 +240,7 @@ ] }, { - "name": "Python: debug_exec_test", + "name": "exec_test", "type": "python", "request": "launch", "program": "${file}", diff --git a/command.txt b/command.txt index f6527e5..cc00b41 100644 --- a/command.txt +++ b/command.txt @@ -37,3 +37,159 @@ pip install pyinstaller C:\Development\Anaconda3\envs\vmdsizing_np64\Lib\site-packages\bezier\extra-dll̒gdllefBNgɔzuiKwƌ‚Ȃ߁j -------------------------- + +[cypthon] + +conda create -n vmdsizing_cython python=3.8 + +pip install cython +pip install numpy +pip install wxpython +pip install numpy-quaternion +pip install bezier +pip install pprofile +pip install pypiwin32 + +conda install pyinstaller + +C:\Development\Anaconda3\envs\vmdsizing_cython\Lib\site-packages\bezier\extra-dll ̒gdllefBNgɔzuiKwƌ‚Ȃ߁j + +lessƂŝ蕪U +4047b + +-------------------------- + +[cypthon + numba] + +pip install cython +pip install numpy +pip install wxpython +pip install numpy-quaternion +pip install bezier +conda install numba + +conda create -n vmdsizing_numba python=3.8 + +pip install pprofile + +conda create -n vmdsizing_numba_exe python=3.8 + +conda install pyinstaller + +C:\Development\Anaconda3\envs\vmdsizing_cython\Lib\site-packages\bezier\extra-dll ̒gdllefBNgɔzuiKwƌ‚Ȃ߁j + +"C:\Development\Anaconda3\Lib\site-packages\win32\pythoncom38.dll" +"C:\Development\Anaconda3\Lib\site-packages\win32\pywintypes38.dll" + +to + +C:\Development\Anaconda3\Lib\site-packages\win32\lib + + + + + + + + + + + +-------------------------- + +pypy + +conda remove -n vmdsizing_np64_pypy2 --all + +set CONDA_FORCE_32BIT=1 + +conda create -n vmdsizing_np64_pypy2 python=3.6.9 + +conda activate vmdsizing_np64_pypy1 + +python -m ensurepip + +pip install numpy +pip install numpy-quaternion + +pip install wheel +pip install wxPython + +https://pypi.org/project/wxPython/#files +tar.gzDL +build.pyC + +pip install "E:\WebDownload\wxPython-4.1.0.zip" + + +pip install bezier +pip install pyinstaller + + + + + +conda create -n vmdsizing_np64_pypy8 pypy cudatoolkit=9.0 + + +conda activate vmdsizing_np64_pypy7 + + +conda install -c conda-forge pypy + + + + + + +python -m pip +python -m ensurepip +pip install wheel + + +conda config --append channels conda-forge + +conda install numpy + +pip install numpy-quaternion + +pip install wxPython +pip install bezier==2020.1.14 +pip install pypiwin32 +conda install pyinstaller + + + + +unlink python +ln -s pypy3 python + +pypy܂Ə㏑ + +apt-get update +apt-get install build-essential + +sudo apt install libsdl2-2.0-0 libsdl2-dev +conda install wxPython +pip install pep517 +pip install numpy-quaternion + +sudo apt-get install python3-dev + +$ BEZIER_NO_EXTENSION=true \ +> python -m pip install --upgrade bezier --no-binary=bezier +pip install bezier==2020.1.14 + +pip install pypiwin32 +pip install pyinstaller + +sudo apt install cifs-utils + +python -m pip +python -m ensurepip +python -m pip install -U pip +pip install --upgrade pip + +pip install wheel +python -m pip install -U pip +pip install --upgrade pip diff --git a/gui.bat b/gui.bat index 32dcd45..c6e4fa1 100644 --- a/gui.bat +++ b/gui.bat @@ -8,5 +8,5 @@ cd /d %~dp0 cls -activate vmdsizing_np && python src\executor.py --out_log 1 --verbose 20 --is_saving 0 +activate vmdsizing_cython && src\setup_install.bat && python src\executor.py --out_log 1 --verbose 20 --is_saving 1 diff --git a/pyinstaller64.bat b/pyinstaller64.bat index 5955256..b66c8b5 100644 --- a/pyinstaller64.bat +++ b/pyinstaller64.bat @@ -8,4 +8,6 @@ cd /d %~dp0 cls -activate vmdsizing_np64_3 && pyinstaller --clean vmdising_np64.spec +activate vmdsizing_cython_exe1 && src\setup_install.bat && pyinstaller --clean vmdising_np64.spec + + diff --git a/smooth.bat b/smooth.bat new file mode 100644 index 0000000..39778a4 --- /dev/null +++ b/smooth.bat @@ -0,0 +1,25 @@ +@echo off +rem --- +rem --- X[WO +rem --- + +rem --- JgfBNgsɕύX +cd /d %~dp0 + +cls + +set MOTION_PATH="D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_1l\tIII Ȃ‚\nac_aikotoba3_300-468_Ԏ~N_W_T_20200917_222601.vmd" +set MODEL_PATH=D:\MMD\MikuMikuDance_v926x64\UserFile\Model\VOCALOID\~N\Ԏ~N\Ԏ~N_W.pmx +set LOOP_CNT=2 +set INTERPOLATION=0 +set BONE_LIST="Er;Er;EЂ;E蝀;E;r;r;Ђ;蝀;;" +rem set BONE_LIST="EЂ" +rem set BONE_LIST="Er" +rem set BONE_LIST="" + +set VERBOSE="10" + + +activate vmdsizing_cython && src\setup.bat && python src/executor_smooth.py --motion_path %MOTION_PATH% --model_path %MODEL_PATH% --loop_cnt %LOOP_CNT% --interpolation %INTERPOLATION% --bone_list %BONE_LIST% --verbose %VERBOSE% + + diff --git a/src/setup.bat b/src/setup.bat new file mode 100644 index 0000000..28749f3 --- /dev/null +++ b/src/setup.bat @@ -0,0 +1,13 @@ +@echo off +cls + +cd /d %~dp0 + +rem -- svt@Cp +rem kernprof -l setup.py build_ext --inplace + + +rem -- ʏp +python setup.py build_ext --inplace + +cd .. diff --git a/src/setup.py b/src/setup.py new file mode 100644 index 0000000..efa1540 --- /dev/null +++ b/src/setup.py @@ -0,0 +1,14 @@ +from setuptools import setup +from Cython.Distutils import build_ext +from Cython.Build import cythonize +import setup_ext + +from Cython.Compiler.Options import get_directive_defaults +directive_defaults = get_directive_defaults() +directive_defaults['linetrace'] = True +directive_defaults['binding'] = True + +setup(name="*", cmdclass={"build_ext": build_ext}, ext_modules=cythonize(setup_ext.get_ext(), annotate=True, \ + compiler_directives={'language_level': "3", 'profile': True, 'linetrace': True, 'binding': True}, **setup_ext.kwargs)) + + diff --git a/src/setup_ext.py b/src/setup_ext.py new file mode 100644 index 0000000..5be5110 --- /dev/null +++ b/src/setup_ext.py @@ -0,0 +1,53 @@ +from setuptools import Extension +from numpy import get_include # cimport numpy を使うため + +bezier_path = 'C:/Development/Anaconda3/envs/vmdsizing_cython_exe1/Lib/site-packages/bezier/include' + +kwargs = {"output_dir": "./build/output", "build_dir": "./build/"} + + +def get_ext(): + ext = [] + sources = ["module\\MMath.py", "module\\MParams.py"] + # for path in glob.glob("*/**/*.pyx", recursive=True): + # if os.path.isfile(path): + # print(path) + # sources.append(path) + # for path in glob.glob("*/**/*.py", recursive=True): + # if "__init__" not in path and os.path.isfile(path): + # sources.append(path) + + for source in sources: + path = source.replace("\\", ".").replace(".pyx", "").replace(".py", "") + print("%s -> %s" % (source, path)) + ext.append(Extension(path, sources=[source], include_dirs=['.', bezier_path, get_include()], define_macros=[("NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION")])) + + return ext + +# ext = [Extension("module.MMath", sources=["module/MMath.py"], include_dirs=['.', bezier_path, get_include()]), \ +# # Extension("module.MMath", sources=["module/MMath.py"], include_dirs=['.', bezier_path, get_include()], define_macros=[('CYTHON_TRACE', '1')]), \ +# Extension("module.MParams", sources=["module/MParams.py"], include_dirs=['.', bezier_path, get_include()]), \ +# Extension("module.MOptions", sources=["module/MOptions.py"], include_dirs=['.', bezier_path, get_include()]), \ +# Extension("utils.MBezierUtils", sources=["utils/MBezierUtils.py"], include_dirs=['.', bezier_path, get_include()]), \ +# Extension("utils.MLogger", sources=["utils/MLogger.py"], include_dirs=['.', bezier_path, get_include()]), \ +# # Extension("utils.MServiceUtils", sources=["utils/MServiceUtils.py"], include_dirs=['.', bezier_path, get_include()], define_macros=[('CYTHON_TRACE', '1')]), \ +# Extension("utils.MServiceUtils", sources=["utils/MServiceUtils.py"], include_dirs=['.', bezier_path, get_include()]), \ +# Extension("mmd.PmxData", sources=["mmd/PmxData.py"], include_dirs=['.', bezier_path, get_include()]), \ +# Extension("mmd.PmxReader", sources=["mmd/PmxReader.py"], include_dirs=['.', bezier_path, get_include()]), \ +# # Extension("mmd.VmdData", sources=["mmd/VmdData.py"], include_dirs=['.', bezier_path, get_include()], define_macros=[('CYTHON_TRACE', '1')]), \ +# Extension("mmd.VmdData", sources=["mmd/VmdData.py"], include_dirs=['.', bezier_path, get_include()]), \ +# Extension("mmd.VmdReader", sources=["mmd/VmdReader.py"], include_dirs=['.', bezier_path, get_include()]), \ +# # Extension("mmd.VpdReader", sources=["mmd/VpdReader.py"], include_dirs=['.', bezier_path, get_include()]), \ +# # Extension("mmd.VmdWriter", sources=["mmd/VmdWriter.py"], include_dirs=['.', bezier_path, get_include()]), \ +# # Extension("service.parts.MoveService", sources=["service/parts/MoveService.py"], include_dirs=['.', bezier_path, get_include()]), \ +# # Extension("service.parts.CameraService", sources=["service/parts/CameraService.py"], include_dirs=['.', bezier_path, get_include()]), \ +# # Extension("service.parts.MorphService", sources=["service/parts/MorphService.py"], include_dirs=['.', bezier_path, get_include()]), \ +# Extension("service.parts.StanceService", sources=["service/parts/StanceService.py"], include_dirs=['.', bezier_path, get_include()]), \ +# # Extension("service.parts.ArmAvoidanceService", sources=["service/parts/ArmAvoidanceService.py"], include_dirs=['.', bezier_path, get_include()], define_macros=[('CYTHON_TRACE', '1')]), \ +# Extension("service.parts.ArmAvoidanceService", sources=["service/parts/ArmAvoidanceService.py"], include_dirs=['.', bezier_path, get_include()]), \ +# Extension("service.parts.ArmAlignmentService", sources=["service/parts/ArmAlignmentService.py"], include_dirs=['.', bezier_path, get_include()]), \ +# Extension("service.SizingService", sources=["service/SizingService.py"], include_dirs=['.', bezier_path, get_include()]), \ +# # Extension("service.ConvertSmoothService", sources=["service/ConvertSmoothService.py"], include_dirs=['.', bezier_path, get_include()]), \ +# ] + + diff --git a/src/setup_install.bat b/src/setup_install.bat new file mode 100644 index 0000000..418b268 --- /dev/null +++ b/src/setup_install.bat @@ -0,0 +1,10 @@ +@echo off +cls + +cd /d %~dp0 + +python setup_install.py clean + +python setup_install.py build_ext --force --inplace + +cd .. diff --git a/src/setup_install.py b/src/setup_install.py new file mode 100644 index 0000000..bdf98ef --- /dev/null +++ b/src/setup_install.py @@ -0,0 +1,9 @@ +from setuptools import setup +from Cython.Distutils import build_ext +from Cython.Build import cythonize +import setup_ext + +setup(name="*", cmdclass={"build_ext": build_ext}, ext_modules=cythonize(setup_ext.get_ext(), \ + compiler_directives={'language_level': "3"}, **setup_ext.kwargs)) + + diff --git a/vmdising_np64.spec b/vmdising_np64.spec index efc6107..5386e74 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -9,10 +9,10 @@ a = Analysis(['src\\executor.py'], pathex=[], binaries=[], datas=[], - hiddenimports=['wx._adv', 'wx._html', 'pkg_resources.py2_warn'], + hiddenimports=['wx._adv', 'wx._html', 'bezier', 'quaternion'], hookspath=[], runtime_hooks=[], - excludes=['mkl','libopenblas'], + excludes=['mkl','libopenblas', 'tkinter', 'pkg_resources', 'win32comgenpy', 'traitlets', 'PIL', 'IPython', 'xml', 'pydoc', 'lib2to3', 'pygments', 'matplotlib'], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=block_cipher, @@ -26,12 +26,12 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.00_64bit', + name='VmdSizing_5.01_β35_64bit', debug=False, bootloader_ignore_signals=False, strip=False, upx=True, runtime_tmpdir=None, - console=False, + console=True, icon='.\\src\\vmdsizing.ico') From 609f80ac07f3187eb05c33e74086c571a05b6acc Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Fri, 25 Sep 2020 09:20:34 +0900 Subject: [PATCH 02/37] =?UTF-8?q?5.01=5F=CE=B236?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ・v5からブランチ再作成 ・数学ライブラリのコンパイルのみ実施(コードはPythonのまま) --- src/executor.py | 2 +- vmdising_np64.spec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/executor.py b/src/executor.py index 07f9e4c..8971ab1 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00" +VERSION_NAME = "ver5.00_β36" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/vmdising_np64.spec b/vmdising_np64.spec index 5386e74..6efcc44 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,7 +26,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β35_64bit', + name='VmdSizing_5.01_β36_64bit', debug=False, bootloader_ignore_signals=False, strip=False, From 1e6cc89b43a1832c0d50299d03e81eed2952164e Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Fri, 25 Sep 2020 09:58:59 +0900 Subject: [PATCH 03/37] =?UTF-8?q?5.01=5F=CE=B237?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VmdSizing_5.01_β37 miumiu ・UIのみ既存から移植  ・左下の進捗数欄 → クリックした時の進捗ダイアログ  ・デバッグ版の一括出力  ・スムージングの出力ボーン指定 --- gui.bat | 2 +- src/executor.py | 2 +- src/form/__init__.py | 0 src/form/panel/BlendPanel.py | 8 +- src/form/panel/CsvPanel.py | 9 +- src/form/panel/FilePanel.py | 126 +++++++++++++++--- src/form/panel/SmoothPanel.py | 162 ++++++++++++++++++++++- src/form/panel/VmdPanel.py | 9 +- src/form/panel/__init__.py | 0 src/form/parts/ConsoleCtrl.py | 23 +++- src/form/parts/StatusCtrl.py | 27 ++++ src/form/parts/__init__.py | 0 src/form/worker/SizingWorkerThread.py | 111 +++++++++++++--- src/form/worker/SmoothWorkerThread.py | 1 + src/form/worker/__init__.py | 0 src/mmd/__init__.py | 0 src/module/MOptions.py | 8 +- src/module/__init__.py | 0 src/service/ConvertSmoothService.py | 10 +- src/service/ConvertVmdService.py | 4 +- src/service/MorphBlendService.py | 2 +- src/service/SizingService.py | 10 +- src/service/__init__.py | 0 src/service/parts/ArmAlignmentService.py | 6 + src/service/parts/ArmAvoidanceService.py | 10 ++ src/service/parts/CameraService.py | 9 +- src/service/parts/MorphService.py | 10 +- src/service/parts/MoveService.py | 8 ++ src/service/parts/StanceService.py | 85 +++++++++++- src/service/parts/__init__.py | 0 src/utils/__init__.py | 0 vmdising_np64.spec | 2 +- 32 files changed, 575 insertions(+), 69 deletions(-) create mode 100644 src/form/__init__.py create mode 100644 src/form/panel/__init__.py create mode 100644 src/form/parts/StatusCtrl.py create mode 100644 src/form/parts/__init__.py create mode 100644 src/form/worker/__init__.py create mode 100644 src/mmd/__init__.py create mode 100644 src/module/__init__.py create mode 100644 src/service/__init__.py create mode 100644 src/service/parts/__init__.py create mode 100644 src/utils/__init__.py diff --git a/gui.bat b/gui.bat index c6e4fa1..25bee50 100644 --- a/gui.bat +++ b/gui.bat @@ -8,5 +8,5 @@ cd /d %~dp0 cls -activate vmdsizing_cython && src\setup_install.bat && python src\executor.py --out_log 1 --verbose 20 --is_saving 1 +activate vmdsizing_cython && src\setup.bat && python src\executor.py --out_log 1 --verbose 20 --is_saving 1 diff --git a/src/executor.py b/src/executor.py index 8971ab1..9d7f838 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00_β36" +VERSION_NAME = "ver5.00_β37" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/form/__init__.py b/src/form/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/form/panel/BlendPanel.py b/src/form/panel/BlendPanel.py index d62e774..13741b6 100644 --- a/src/form/panel/BlendPanel.py +++ b/src/form/panel/BlendPanel.py @@ -127,7 +127,7 @@ def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int): self.sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER | wx.SHAPED, 5) # コンソール - self.console_ctrl = ConsoleCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(-1, 420), \ + self.console_ctrl = ConsoleCtrl(self, self.frame.logging_level, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(-1, 420), \ wx.TE_MULTILINE | wx.TE_READONLY | wx.BORDER_NONE | wx.HSCROLL | wx.VSCROLL | wx.WANTS_CHARS) self.console_ctrl.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT)) self.console_ctrl.Bind(wx.EVT_CHAR, lambda event: MFormUtils.on_select_all(event, self.console_ctrl)) @@ -231,7 +231,8 @@ def on_convert_blend(self, event: wx.Event): # フォーム有効化 self.enable() # 出力先をデフォルトに戻す - sys.stdout = self.frame.file_panel_ctrl.console_ctrl + if sys.stdout != self.frame.file_panel_ctrl.console_ctrl: + sys.stdout = self.frame.file_panel_ctrl.console_ctrl return result @@ -272,7 +273,8 @@ def on_convert_blend_result(self, event: wx.Event): logger.info("モーフブレンドが完了しました", decoration=MLogger.DECORATION_BOX, title="OK") # 出力先をデフォルトに戻す - sys.stdout = self.frame.file_panel_ctrl.console_ctrl + if sys.stdout != self.frame.file_panel_ctrl.console_ctrl: + sys.stdout = self.frame.file_panel_ctrl.console_ctrl def is_valid(self): if len(self.morph_eye_list.GetSelections()) + len(self.morph_eyebrow_list.GetSelections()) \ diff --git a/src/form/panel/CsvPanel.py b/src/form/panel/CsvPanel.py index 2cd9ac6..bf0a8ac 100644 --- a/src/form/panel/CsvPanel.py +++ b/src/form/panel/CsvPanel.py @@ -47,7 +47,7 @@ def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int): self.sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER | wx.SHAPED, 5) # コンソール - self.console_ctrl = ConsoleCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(-1, 420), \ + self.console_ctrl = ConsoleCtrl(self, self.frame.logging_level, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(-1, 420), \ wx.TE_MULTILINE | wx.TE_READONLY | wx.BORDER_NONE | wx.HSCROLL | wx.VSCROLL | wx.WANTS_CHARS) self.console_ctrl.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT)) self.console_ctrl.Bind(wx.EVT_CHAR, lambda event: MFormUtils.on_select_all(event, self.console_ctrl)) @@ -98,7 +98,8 @@ def on_convert_csv(self, event: wx.Event): # フォーム有効化 self.enable() # 出力先をデフォルトに戻す - sys.stdout = self.frame.file_panel_ctrl.console_ctrl + if sys.stdout != self.frame.file_panel_ctrl.console_ctrl: + sys.stdout = self.frame.file_panel_ctrl.console_ctrl return result @@ -139,4 +140,6 @@ def on_convert_csv_result(self, event: wx.Event): logger.info("CSV変換が完了しました", decoration=MLogger.DECORATION_BOX, title="OK") # 出力先をデフォルトに戻す - sys.stdout = self.frame.file_panel_ctrl.console_ctrl + if sys.stdout != self.frame.file_panel_ctrl.console_ctrl: + sys.stdout = self.frame.file_panel_ctrl.console_ctrl + diff --git a/src/form/panel/FilePanel.py b/src/form/panel/FilePanel.py index 12adb65..3a94377 100644 --- a/src/form/panel/FilePanel.py +++ b/src/form/panel/FilePanel.py @@ -1,12 +1,15 @@ # -*- coding: utf-8 -*- # import wx +from wx.core import TreeItemId import wx.lib.newevent import sys +import os from form.panel.BasePanel import BasePanel from form.parts.SizingFileSet import SizingFileSet from form.parts.ConsoleCtrl import ConsoleCtrl +from form.parts.StatusCtrl import StatusCtrl from module.MMath import MRect, MVector3D, MVector4D, MQuaternion, MMatrix4x4 # noqa from utils import MFormUtils, MFileUtils # noqa from utils.MLogger import MLogger # noqa @@ -21,6 +24,7 @@ def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int, file_hito super().__init__(frame, parent, tab_idx) self.file_hitories = file_hitories self.timer = None + self.tree_process_dict = {} # ファイルセット self.file_set = SizingFileSet(frame, self, self.file_hitories, 1) @@ -45,19 +49,61 @@ def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int, file_hito self.sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER | wx.SHAPED, 5) # コンソール - self.console_ctrl = ConsoleCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(-1, -1), \ + self.console_ctrl = ConsoleCtrl(self, self.frame.logging_level, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(-1, -1), \ wx.TE_MULTILINE | wx.TE_READONLY | wx.BORDER_NONE | wx.HSCROLL | wx.VSCROLL | wx.WANTS_CHARS) self.console_ctrl.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT)) self.console_ctrl.Bind(wx.EVT_CHAR, lambda event: MFormUtils.on_select_all(event, self.console_ctrl)) self.sizer.Add(self.console_ctrl, 1, wx.ALL | wx.EXPAND, 5) + status_sizer = wx.BoxSizer(wx.HORIZONTAL) + + # 進捗ダイアログ + self.process_dialog = None + + # 進捗ステータス + self.before_bracket_ctrl = wx.TextCtrl(self, wx.ID_ANY, "(", wx.DefaultPosition, wx.Size(5, -1), wx.TE_READONLY | wx.BORDER_NONE | wx.WANTS_CHARS) + self.before_bracket_ctrl.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT)) + status_sizer.Add(self.before_bracket_ctrl, 0, wx.ALIGN_LEFT, 5) + + self.now_process_ctrl = StatusCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(20, -1), wx.TE_READONLY | wx.BORDER_NONE | wx.WANTS_CHARS) + self.now_process_ctrl.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT)) + self.now_process_ctrl.SetToolTip(u"現在進んでいるの大まかな処理数です。クリックすると、具体的な処理進捗がダイアログで表示されます。") + self.now_process_ctrl.Bind(wx.EVT_LEFT_DOWN, self.show_process_dialog) + status_sizer.Add(self.now_process_ctrl, 0, wx.ALIGN_LEFT, 5) + + self.slash_ctrl = wx.TextCtrl(self, wx.ID_ANY, "/", wx.DefaultPosition, wx.Size(5, -1), wx.TE_READONLY | wx.BORDER_NONE | wx.WANTS_CHARS) + self.slash_ctrl.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT)) + status_sizer.Add(self.slash_ctrl, 0, wx.ALIGN_LEFT, 5) + + self.total_process_ctrl = StatusCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(20, -1), wx.TE_READONLY | wx.BORDER_NONE | wx.WANTS_CHARS) + self.total_process_ctrl.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT)) + self.total_process_ctrl.SetToolTip(u"全体の大まかな処理数です。クリックすると、具体的な処理進捗がダイアログで表示されます。") + self.total_process_ctrl.Bind(wx.EVT_LEFT_DOWN, self.show_process_dialog) + status_sizer.Add(self.total_process_ctrl, 0, wx.ALIGN_LEFT, 5) + + self.after_bracket_ctrl = wx.TextCtrl(self, wx.ID_ANY, ")", wx.DefaultPosition, wx.Size(5, -1), wx.TE_READONLY | wx.BORDER_NONE | wx.WANTS_CHARS) + self.after_bracket_ctrl.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT)) + status_sizer.Add(self.after_bracket_ctrl, 0, wx.ALIGN_LEFT, 5) + # ゲージ - self.gauge_ctrl = wx.Gauge(self, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.GA_HORIZONTAL) + self.gauge_ctrl = wx.Gauge(self, wx.ID_ANY, 100, wx.DefaultPosition, wx.Size(550, -1), wx.GA_HORIZONTAL) self.gauge_ctrl.SetValue(0) - self.sizer.Add(self.gauge_ctrl, 0, wx.ALL | wx.EXPAND, 5) + status_sizer.Add(self.gauge_ctrl, 0, wx.ALL | wx.EXPAND, 5) + + self.sizer.Add(status_sizer, 0, wx.ALL, 0) self.fit() + def show_process_dialog(self, event: wx.Event): + if self.process_dialog: + # 既にある場合、一旦破棄 + self.process_dialog.Destroy() + + self.process_dialog = ProcessDialog(self.frame, self) + self.process_dialog.Show() + + event.Skip() + # マルチプロセス用flush def print(self, txt): print(txt) @@ -88,6 +134,8 @@ def on_check_click(self, event: wx.Event): # 実行前チェック def on_check(self, event: wx.Event): + self.timer.Stop() + self.Unbind(wx.EVT_TIMER, id=TIMER_ID) # 出力先をファイルパネルのコンソールに変更 sys.stdout = self.console_ctrl @@ -106,8 +154,6 @@ def on_check(self, event: wx.Event): # プログレス非表示 self.gauge_ctrl.SetValue(0) - self.timer.Stop() - logger.warning("読み込み処理を中断します。", decoration=MLogger.DECORATION_BOX) event.Skip(False) @@ -122,15 +168,11 @@ def on_check(self, event: wx.Event): # 履歴保持 self.save() - self.timer.Stop() - # 一旦読み込み(そのままチェック) self.frame.load(event, target_idx=0) event.Skip() else: - self.timer.Stop() - logger.error("まだ処理が実行中です。終了してから再度実行してください。", decoration=MLogger.DECORATION_BOX) event.Skip(False) @@ -141,6 +183,10 @@ def on_exec_click(self, event: wx.Event): # サイジング実行 def on_exec(self, event: wx.Event): + if self.timer: + self.timer.Stop() + self.Unbind(wx.EVT_TIMER, id=TIMER_ID) + # 出力先をファイルパネルのコンソールに変更 sys.stdout = self.console_ctrl @@ -159,8 +205,6 @@ def on_exec(self, event: wx.Event): # プログレス非表示 self.gauge_ctrl.SetValue(0) - self.timer.Stop() - logger.warning("VMDサイジングを中断します。", decoration=MLogger.DECORATION_BOX) event.Skip(False) @@ -175,15 +219,11 @@ def on_exec(self, event: wx.Event): # 履歴保持 self.save() - self.timer.Stop() - # サイジング可否チェックの後に実行 self.frame.load(event, is_exec=True, target_idx=0) event.Skip() else: - self.timer.Stop() - logger.error("まだ処理が実行中です。終了してから再度実行してください。", decoration=MLogger.DECORATION_BOX) event.Skip(False) @@ -210,3 +250,59 @@ def save(self): # JSON出力 MFileUtils.save_history(self.frame.mydir_path, self.frame.file_hitories) + + +class ProcessDialog(wx.Dialog): + + def __init__(self, frame: wx.Frame, panel: wx.Panel): + super().__init__(frame, id=wx.ID_ANY, title="進捗ダイアログ", pos=(-1, -1), size=(700, 450), style=wx.DEFAULT_DIALOG_STYLE | wx.STAY_ON_TOP) + + self.frame = frame + self.panel = panel + + self.sizer = wx.BoxSizer(wx.VERTICAL) + + # データツリー + self.tree_ctrl = wx.TreeCtrl(self, id=wx.ID_ANY, pos=(-1, -1), size=(650, 400), style=wx.TR_ROW_LINES) + # 初期化 + self.initialize(self.panel.tree_process_dict) + + self.sizer.Add(self.tree_ctrl, 0, wx.ALL, 5) + + self.SetSizer(self.sizer) + self.sizer.Layout() + + # 画面中央に表示 + self.CentreOnScreen() + + # 最初は隠しておく + self.Hide() + + # 初期化 + def initialize(self, tree_dict: dict): + # Root + tr_root_ctrl = self.tree_ctrl.AddRoot(text="VMDサイジング") + + # ツリー追加 + self.append_tree(tree_dict, tr_root_ctrl) + + # ツリー追加 + def append_tree(self, item_dict: dict, parent_ctrl: TreeItemId): + for tk, tv in item_dict.items(): + if isinstance(tv, bool) and tv: + # 処理が終了している場合、アイコン追加 + display_ctrl = self.tree_ctrl.AppendItem(parent=parent_ctrl, text=("○ {0}".format(tk))) + self.tree_ctrl.SetItemTextColour(display_ctrl, "BLUE") + elif isinstance(tv, bool) and not tv: + # 終了していない場合 + display_ctrl = self.tree_ctrl.AppendItem(parent=parent_ctrl, text=("- {0}".format(tk))) + self.tree_ctrl.SetItemTextColour(display_ctrl, "GREY") + else: + display_ctrl = self.tree_ctrl.AppendItem(parent=parent_ctrl, text=tk) + + if isinstance(tv, dict): + # 下位が辞書の場合、ループ再帰 + self.append_tree(tv, display_ctrl) + + self.tree_ctrl.ExpandAllChildren(parent_ctrl) + diff --git a/src/form/panel/SmoothPanel.py b/src/form/panel/SmoothPanel.py index b56cb7d..4226437 100644 --- a/src/form/panel/SmoothPanel.py +++ b/src/form/panel/SmoothPanel.py @@ -2,6 +2,7 @@ # import os import wx +import wx.dataview import sys from form.panel.BasePanel import BasePanel @@ -36,14 +37,14 @@ def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int): # 対象VMDファイルコントロール self.smooth_vmd_file_ctrl = HistoryFilePickerCtrl(self.frame, self, u"対象モーションVMD", u"対象モーションVMDファイルを開く", ("vmd"), wx.FLP_DEFAULT_STYLE, \ u"調整したい対象モーションのVMDパスを指定してください。\nD&Dでの指定、開くボタンからの指定、履歴からの選択ができます。", \ - file_model_spacer=0, title_parts_ctrl=None, title_parts2_ctrl=None, file_histories_key="smooth_vmd", is_change_output=True, \ + file_model_spacer=46, title_parts_ctrl=None, title_parts2_ctrl=None, file_histories_key="smooth_vmd", is_change_output=True, \ is_aster=False, is_save=False, set_no=1) self.header_sizer.Add(self.smooth_vmd_file_ctrl.sizer, 1, wx.EXPAND, 0) # 対象PMXファイルコントロール self.smooth_model_file_ctrl = HistoryFilePickerCtrl(self.frame, self, u"適用モデルPMX", u"適用モデルPMXファイルを開く", ("pmx"), wx.FLP_DEFAULT_STYLE, \ u"モーションを適用したいモデルのPMXパスを指定してください。\n人体モデル以外にも適用可能です。\nD&Dでの指定、開くボタンからの指定、履歴からの選択ができます。", \ - file_model_spacer=0, title_parts_ctrl=None, title_parts2_ctrl=None, file_histories_key="smooth_pmx", \ + file_model_spacer=52, title_parts_ctrl=None, title_parts2_ctrl=None, file_histories_key="smooth_pmx", \ is_change_output=True, is_aster=False, is_save=False, set_no=1) self.header_sizer.Add(self.smooth_model_file_ctrl.sizer, 1, wx.EXPAND, 0) @@ -55,6 +56,20 @@ def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int): self.sizer.Add(self.header_sizer, 0, wx.EXPAND | wx.ALL, 5) + self.target_sizer = wx.BoxSizer(wx.HORIZONTAL) + + # ボーン名指定 + self.bone_target_txt_ctrl = wx.TextCtrl(self, wx.ID_ANY, "", wx.DefaultPosition, (450, 50), wx.HSCROLL | wx.VSCROLL | wx.TE_MULTILINE | wx.TE_READONLY) + self.bone_target_txt_ctrl.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT)) + self.target_sizer.Add(self.bone_target_txt_ctrl, 1, wx.EXPAND | wx.ALL, 5) + + self.bone_target_btn_ctrl = wx.Button(self, wx.ID_ANY, u"ボーン指定", wx.DefaultPosition, wx.DefaultSize, 0) + self.bone_target_btn_ctrl.SetToolTip(u"モーションに登録されているボーンから、スムージングにかけたいボーンを指定できます") + self.bone_target_btn_ctrl.Bind(wx.EVT_BUTTON, self.on_click_bone_target) + self.target_sizer.Add(self.bone_target_btn_ctrl, 0, wx.ALIGN_BOTTOM | wx.ALL, 5) + + self.sizer.Add(self.target_sizer, 0, wx.EXPAND | wx.ALL, 5) + self.setting_sizer = wx.BoxSizer(wx.HORIZONTAL) # 処理回数 @@ -91,7 +106,7 @@ def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int): self.sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER | wx.SHAPED, 5) # コンソール - self.console_ctrl = ConsoleCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(-1, 420), \ + self.console_ctrl = ConsoleCtrl(self, self.frame.logging_level, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(-1, 420), \ wx.TE_MULTILINE | wx.TE_READONLY | wx.BORDER_NONE | wx.HSCROLL | wx.VSCROLL | wx.WANTS_CHARS) self.console_ctrl.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT)) self.console_ctrl.Bind(wx.EVT_CHAR, lambda event: MFormUtils.on_select_all(event, self.console_ctrl)) @@ -105,6 +120,10 @@ def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int): self.Layout() self.fit() + # ボーン選択用ダイアログ + self.bone_dialog = TargetBoneDialog(self.frame, self) + self.bone_list = [] + # フレームに変換完了処理バインド self.frame.Bind(EVT_SMOOTH_THREAD, self.on_convert_smooth_result) @@ -174,7 +193,8 @@ def on_convert_smooth(self, event: wx.Event): # フォーム有効化 self.enable() # 出力先をデフォルトに戻す - sys.stdout = self.frame.file_panel_ctrl.console_ctrl + if sys.stdout != self.frame.file_panel_ctrl.console_ctrl: + sys.stdout = self.frame.file_panel_ctrl.console_ctrl return result @@ -188,8 +208,6 @@ def on_convert_smooth(self, event: wx.Event): return result - event.Skip() - # スムージング変換完了処理 def on_convert_smooth_result(self, event: wx.Event): self.elapsed_time = event.elapsed_time @@ -208,7 +226,8 @@ def on_convert_smooth_result(self, event: wx.Event): self.gauge_ctrl.SetValue(0) # 出力先をデフォルトに戻す - sys.stdout = self.frame.file_panel_ctrl.console_ctrl + if sys.stdout != self.frame.file_panel_ctrl.console_ctrl: + sys.stdout = self.frame.file_panel_ctrl.console_ctrl def show_worked_time(self): # 経過秒数を時分秒に変換 @@ -220,3 +239,132 @@ def show_worked_time(self): worked_time = "{0:02d}分{1:02d}秒".format(int(td_m), int(td_s)) return worked_time + + def on_click_bone_target(self, event: wx.Event): + self.disable() + + sys.stdout = self.console_ctrl + # VMD読み込み + self.smooth_vmd_file_ctrl.load() + # PMX読み込み + self.smooth_model_file_ctrl.load() + + if (self.smooth_vmd_file_ctrl.data and self.smooth_model_file_ctrl.data and \ + (self.smooth_vmd_file_ctrl.data.digest != self.bone_dialog.vmd_digest or self.smooth_model_file_ctrl.data.digest != self.bone_dialog.pmx_digest)): + + # データが揃ってたら押下可能 + self.bone_target_btn_ctrl.Enable() + # リストクリア + self.bone_target_txt_ctrl.SetValue("") + # ボーン選択用ダイアログ + self.bone_dialog.Destroy() + self.bone_dialog = TargetBoneDialog(self.frame, self) + else: + if not self.smooth_vmd_file_ctrl.data or not self.smooth_model_file_ctrl.data: + logger.error("対象モーションVMDもしくは適用モデルPMXが未指定です。", decoration=MLogger.DECORATION_BOX) + self.enable() + return + + self.enable() + + if self.bone_dialog.ShowModal() == wx.ID_CANCEL: + return # the user changed their mind + + # 選択されたボーンリストを入力欄に設定 + self.bone_list = self.bone_dialog.get_bone_list() + self.bone_target_txt_ctrl.SetValue(', '.join(self.bone_list)) + + self.bone_dialog.Hide() + + def get_bone_list(self): + return self.bone_list + + +class TargetBoneDialog(wx.Dialog): + + def __init__(self, frame: wx.Frame, panel: wx.Panel): + super().__init__(frame, id=wx.ID_ANY, title="対象ボーン指定", pos=(-1, -1), size=(700, 450), style=wx.DEFAULT_DIALOG_STYLE, name="TargetBoneDialog") + + self.frame = frame + self.panel = panel + self.vmd_digest = 0 if not self.panel.smooth_vmd_file_ctrl.data else self.panel.smooth_vmd_file_ctrl.data.digest + self.pmx_digest = 0 if not self.panel.smooth_model_file_ctrl.data else self.panel.smooth_model_file_ctrl.data.digest + self.org_bones = [""] # 選択肢文言 + self.rep_bones = [""] + self.org_choices = [] # 選択コントロール + self.rep_mx_choices = [] + self.rep_my_choices = [] + self.rep_mz_choices = [] + self.rep_rx_choices = [] + self.rep_ry_choices = [] + self.rep_rz_choices = [] + + self.sizer = wx.BoxSizer(wx.VERTICAL) + + # 説明文 + self.description_txt = wx.StaticText(self, wx.ID_ANY, u"スムージングしたいボーン名を選択してください。\nCtrlキーを押しながら選択すると、複数ボーンを選択できます。Shiftキーで一括選択できます。", wx.DefaultPosition, wx.DefaultSize, 0) + self.sizer.Add(self.description_txt, 0, wx.ALL, 5) + + # ボタン + self.btn_sizer = wx.BoxSizer(wx.HORIZONTAL) + self.ok_btn = wx.Button(self, wx.ID_OK, "OK") + self.btn_sizer.Add(self.ok_btn, 0, wx.ALL, 5) + + self.calcel_btn = wx.Button(self, wx.ID_CANCEL, "キャンセル") + self.btn_sizer.Add(self.calcel_btn, 0, wx.ALL, 5) + + self.sizer.Add(self.btn_sizer, 0, wx.ALIGN_RIGHT | wx.ALL, 5) + + self.static_line01 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + self.sizer.Add(self.static_line01, 0, wx.EXPAND | wx.ALL, 5) + + # データツリー + self.tree_ctrl = wx.TreeCtrl(self, id=wx.ID_ANY, pos=(-1, -1), size=(650, 400), style=wx.TR_ROW_LINES | wx.TR_MULTIPLE) + + if self.panel.smooth_model_file_ctrl.data and self.panel.smooth_vmd_file_ctrl.data: + model = self.panel.smooth_model_file_ctrl.data + motion = self.panel.smooth_vmd_file_ctrl.data + + tr_root_ctrl = self.tree_ctrl.AddRoot(text=model.name) + + for display_name, display_slot in model.display_slots.items(): + bone_list = [] + for (display_type, bone_idx) in display_slot.references: + if display_type == 0 and bone_idx in model.bone_indexes and model.bone_indexes[bone_idx] in motion.bones.keys(): + # 表示枠にボーンがあって、モーションにもある場合、追加 + bone_list.append(model.bone_indexes[bone_idx]) + + if len(bone_list) > 0: + # 追加対象がある場合、ツリー追加 + + display_ctrl = self.tree_ctrl.AppendItem(parent=tr_root_ctrl, text=display_name) + + for bone_name in bone_list: + self.tree_ctrl.AppendItem(parent=display_ctrl, text=bone_name) + + self.tree_ctrl.Expand(display_ctrl) + + self.tree_ctrl.Expand(tr_root_ctrl) + + self.sizer.Add(self.tree_ctrl, 0, wx.ALL, 5) + + self.SetSizer(self.sizer) + self.sizer.Layout() + + # 画面中央に表示 + self.CentreOnScreen() + + # 最初は隠しておく + self.Hide() + + def get_bone_list(self): + bone_list = [] + + for bone_ctrl_id in self.tree_ctrl.GetSelections(): + bone_list.append(self.tree_ctrl.GetItemText(bone_ctrl_id)) + + return bone_list + + + + diff --git a/src/form/panel/VmdPanel.py b/src/form/panel/VmdPanel.py index e75138d..84ea79a 100644 --- a/src/form/panel/VmdPanel.py +++ b/src/form/panel/VmdPanel.py @@ -64,7 +64,7 @@ def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int): self.sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER | wx.SHAPED, 5) # コンソール - self.console_ctrl = ConsoleCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(-1, 420), \ + self.console_ctrl = ConsoleCtrl(self, self.frame.logging_level, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(-1, 420), \ wx.TE_MULTILINE | wx.TE_READONLY | wx.BORDER_NONE | wx.HSCROLL | wx.VSCROLL | wx.WANTS_CHARS) self.console_ctrl.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT)) self.console_ctrl.Bind(wx.EVT_CHAR, lambda event: MFormUtils.on_select_all(event, self.console_ctrl)) @@ -119,7 +119,8 @@ def on_convert_vmd(self, event: wx.Event): # フォーム有効化 self.enable() # 出力先をデフォルトに戻す - sys.stdout = self.frame.file_panel_ctrl.console_ctrl + if sys.stdout != self.frame.file_panel_ctrl.console_ctrl: + sys.stdout = self.frame.file_panel_ctrl.console_ctrl return result @@ -160,4 +161,6 @@ def on_convert_vmd_result(self, event: wx.Event): logger.info("VMD変換が完了しました", decoration=MLogger.DECORATION_BOX, title="OK") # 出力先をデフォルトに戻す - sys.stdout = self.frame.file_panel_ctrl.console_ctrl + if sys.stdout != self.frame.file_panel_ctrl.console_ctrl: + sys.stdout = self.frame.file_panel_ctrl.console_ctrl + diff --git a/src/form/panel/__init__.py b/src/form/panel/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/form/parts/ConsoleCtrl.py b/src/form/parts/ConsoleCtrl.py index e900fc5..b7c8c1c 100644 --- a/src/form/parts/ConsoleCtrl.py +++ b/src/form/parts/ConsoleCtrl.py @@ -9,12 +9,29 @@ class ConsoleCtrl(wx.TextCtrl): - def __init__(self, parent, id=wx.ID_ANY, value="", pos=wx.DefaultPosition, size=wx.DefaultSize, style=0, validator=wx.DefaultValidator, name=wx.TextCtrlNameStr): + def __init__(self, parent, logging_level, id=wx.ID_ANY, value="", pos=wx.DefaultPosition, size=wx.DefaultSize, style=0, validator=wx.DefaultValidator, name=wx.TextCtrlNameStr): super().__init__(parent, id, value, pos, size, style, validator, name) + self.limit_cnt = 10 - def write(self, text): + if logging_level <= MLogger.DEBUG: + # デバッグ版は纏めて出力 + self.limit_cnt = 5000 + + self.texts = "" + + def write(self, text, stack=False): try: - wx.CallAfter(self.AppendText, text) + self.texts += text + + if len(self.texts) > self.limit_cnt and stack: + # 一定文字数を超えた場合にのみ出力 + wx.CallAfter(self.AppendText, self.texts) + self.texts = "" + elif not stack: + # stackではない場合、そのまま出力 + wx.CallAfter(self.AppendText, self.texts) + self.texts = "" + except: # noqa pass diff --git a/src/form/parts/StatusCtrl.py b/src/form/parts/StatusCtrl.py new file mode 100644 index 0000000..262af24 --- /dev/null +++ b/src/form/parts/StatusCtrl.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# + +import wx +from utils.MLogger import MLogger # noqa + +logger = MLogger(__name__) + + +class StatusCtrl(wx.TextCtrl): + + def __init__(self, parent, id=wx.ID_ANY, value="", pos=wx.DefaultPosition, size=wx.DefaultSize, style=0, validator=wx.DefaultValidator, name=wx.TextCtrlNameStr): + super().__init__(parent, id, value, pos, size, style, validator, name) + + def write(self, text): + try: + wx.CallAfter(self.SetValue, text) + except: # noqa + pass + + # def monitor(self, queue): + # while True: + # # super().write(queue.get()) + # wx.CallAfter(queue.get()) + # # 0.1秒待機 + # time.sleep(0.1) + diff --git a/src/form/parts/__init__.py b/src/form/parts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/form/worker/SizingWorkerThread.py b/src/form/worker/SizingWorkerThread.py index 064cac0..43c9037 100644 --- a/src/form/worker/SizingWorkerThread.py +++ b/src/form/worker/SizingWorkerThread.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- # -import logging import os import time import wx @@ -26,6 +25,7 @@ def __init__(self, frame: wx.Frame, result_event: wx.Event, target_idx: int, is_ self.target_idx = target_idx self.gauge_ctrl = frame.file_panel_ctrl.gauge_ctrl self.options = None + self.output_log_path = None super().__init__(frame, result_event, frame.file_panel_ctrl.console_ctrl) @@ -35,17 +35,56 @@ def thread_event(self): start = time.time() # データセットリスト data_set_list = [] + total_process = 0 + self.frame.file_panel_ctrl.tree_process_dict = {} + + now_camera_output_vmd_path = None + now_camera_data = None + now_camera_path = self.frame.camera_panel_ctrl.camera_vmd_file_ctrl.file_ctrl.GetPath() + if len(now_camera_path) > 0: + now_camera_data = self.frame.camera_panel_ctrl.camera_vmd_file_ctrl.data.copy() + now_camera_output_vmd_path = self.frame.camera_panel_ctrl.output_camera_vmd_file_ctrl.file_ctrl.GetPath() if self.frame.file_panel_ctrl.file_set.motion_vmd_file_ctrl.load(): + + proccess_key = "【No.1】{0}({1})".format( \ + os.path.basename(self.frame.file_panel_ctrl.file_set.motion_vmd_file_ctrl.data.path), \ + self.frame.file_panel_ctrl.file_set.rep_model_file_ctrl.data.name) + + # 1件目のモーションとモデル + self.frame.file_panel_ctrl.tree_process_dict[proccess_key] = {"移動縮尺補正": False} - camera_org_model = self.frame.file_panel_ctrl.file_set.org_model_file_ctrl.data camera_offset_y = 0 - if 1 in self.frame.camera_panel_ctrl.camera_set_dict: - if self.frame.camera_panel_ctrl.camera_set_dict[1].camera_model_file_ctrl.is_set_path(): - # カメラ元モデルが指定されている場合、カメラ元モデル再指定 - camera_org_model = self.frame.camera_panel_ctrl.camera_set_dict[1].camera_model_file_ctrl.data - camera_offset_y = self.frame.camera_panel_ctrl.camera_set_dict[1].camera_offset_y_ctrl.GetValue() + camera_org_model = None + if len(now_camera_path) > 0: + camera_org_model = self.frame.file_panel_ctrl.file_set.org_model_file_ctrl.data + if 1 in self.frame.camera_panel_ctrl.camera_set_dict: + if self.frame.camera_panel_ctrl.camera_set_dict[1].camera_model_file_ctrl.is_set_path(): + # カメラ元モデルが指定されている場合、カメラ元モデル再指定 + camera_org_model = self.frame.camera_panel_ctrl.camera_set_dict[1].camera_model_file_ctrl.data + camera_offset_y = self.frame.camera_panel_ctrl.camera_set_dict[1].camera_offset_y_ctrl.GetValue() + total_process += 2 # 基本補正・腕スタンス補正 + if self.frame.file_panel_ctrl.file_set.org_model_file_ctrl.title_parts_ctrl.GetValue() > 0: + total_process += len(self.frame.file_panel_ctrl.file_set.get_selected_stance_details()) # スタンス追加補正 + self.frame.file_panel_ctrl.tree_process_dict[proccess_key]["スタンス追加補正"] = {} + + for v in self.frame.file_panel_ctrl.file_set.get_selected_stance_details(): + self.frame.file_panel_ctrl.tree_process_dict[proccess_key]["スタンス追加補正"][v] = False + + total_process += self.frame.file_panel_ctrl.file_set.rep_model_file_ctrl.title_parts_ctrl.GetValue() # 捩り分散 + if self.frame.file_panel_ctrl.file_set.rep_model_file_ctrl.title_parts_ctrl.GetValue() == 1: + self.frame.file_panel_ctrl.tree_process_dict[proccess_key]["捩り分散"] = False + + self.frame.file_panel_ctrl.tree_process_dict[proccess_key]["腕スタンス補正"] = False + + if self.frame.arm_panel_ctrl.arm_process_flg_avoidance.GetValue() > 0: + self.frame.file_panel_ctrl.tree_process_dict[proccess_key]["接触回避"] = False + + total_process += min(1, len(self.frame.morph_panel_ctrl.get_morph_list(1))) # モーフ置換 + if len(self.frame.morph_panel_ctrl.get_morph_list(1)) > 0: + self.frame.file_panel_ctrl.tree_process_dict[proccess_key]["モーフ置換"] = False + # 1件目は必ず読み込む first_data_set = MOptionsDataSet( motion=self.frame.file_panel_ctrl.file_set.motion_vmd_file_ctrl.data.copy(), \ @@ -64,6 +103,36 @@ def thread_event(self): # 2件目以降は有効なのだけ読み込む for multi_idx, file_set in enumerate(self.frame.multi_panel_ctrl.file_set_list): if file_set.is_loaded(): + + proccess_key = "【No.{0}】{1}({2})".format( \ + file_set.set_no, \ + os.path.basename(file_set.motion_vmd_file_ctrl.data.path), \ + file_set.rep_model_file_ctrl.data.name) + + # 1件目のモーションとモデル + self.frame.file_panel_ctrl.tree_process_dict[proccess_key] = {"移動縮尺補正": False} + + total_process += 2 # 基本補正・腕スタンス補正 + if file_set.org_model_file_ctrl.title_parts_ctrl.GetValue() > 0: + total_process += len(file_set.get_selected_stance_details()) # スタンス追加補正 + self.frame.file_panel_ctrl.tree_process_dict[proccess_key]["スタンス追加補正"] = {} + + for v in file_set.get_selected_stance_details(): + self.frame.file_panel_ctrl.tree_process_dict[proccess_key]["スタンス追加補正"][v] = False + + total_process += file_set.rep_model_file_ctrl.title_parts_ctrl.GetValue() # 捩り分散 + if file_set.rep_model_file_ctrl.title_parts_ctrl.GetValue() == 1: + self.frame.file_panel_ctrl.tree_process_dict[proccess_key]["捩り分散"] = False + + self.frame.file_panel_ctrl.tree_process_dict[proccess_key]["腕スタンス補正"] = False + + if self.frame.arm_panel_ctrl.arm_process_flg_avoidance.GetValue() > 0: + self.frame.file_panel_ctrl.tree_process_dict[proccess_key]["接触回避"] = False + + total_process += min(1, len(self.frame.morph_panel_ctrl.get_morph_list(file_set.set_no))) # モーフ置換 + + if len(self.frame.morph_panel_ctrl.get_morph_list(file_set.set_no)) > 0: + self.frame.file_panel_ctrl.tree_process_dict[proccess_key]["モーフ置換"] = False camera_offset_y = 0 camera_org_model = file_set.org_model_file_ctrl.data @@ -86,6 +155,15 @@ def thread_event(self): selected_stance_details=file_set.get_selected_stance_details() ) data_set_list.append(multi_data_set) + + total_process += self.frame.arm_panel_ctrl.arm_process_flg_avoidance.GetValue() * len(data_set_list) # 接触回避 + total_process += self.frame.arm_panel_ctrl.arm_process_flg_alignment.GetValue() # 位置合わせ + if self.frame.arm_panel_ctrl.arm_process_flg_alignment.GetValue() > 0: + self.frame.file_panel_ctrl.tree_process_dict["位置合わせ"] = False + + if len(now_camera_path) > 0: + total_process += 1 # カメラ + self.frame.file_panel_ctrl.tree_process_dict["カメラ補正"] = False self.options = MOptions(\ version_name=self.frame.version_name, \ @@ -102,12 +180,17 @@ def thread_event(self): self.frame.arm_panel_ctrl.alignment_distance_floor_slider.GetValue(), \ self.frame.arm_panel_ctrl.arm_check_skip_flg_ctrl.GetValue() ), \ - camera_motion=self.frame.camera_panel_ctrl.camera_vmd_file_ctrl.data, \ - camera_output_vmd_path=self.frame.camera_panel_ctrl.output_camera_vmd_file_ctrl.file_ctrl.GetPath(), \ + camera_motion=now_camera_data, \ + camera_output_vmd_path=now_camera_output_vmd_path, \ monitor=self.frame.file_panel_ctrl.console_ctrl, \ is_file=False, \ outout_datetime=logger.outout_datetime, \ - max_workers=(1 if self.is_exec_saving else min(32, os.cpu_count() + 4))) + max_workers=(1 if self.is_exec_saving else min(32, os.cpu_count() + 4)), \ + total_process=total_process, \ + now_process=0, \ + total_process_ctrl=self.frame.file_panel_ctrl.total_process_ctrl, \ + now_process_ctrl=self.frame.file_panel_ctrl.now_process_ctrl, \ + tree_process_dict=self.frame.file_panel_ctrl.tree_process_dict) self.result = SizingService(self.options).execute() and self.result @@ -120,20 +203,18 @@ def thread_event(self): if self.is_out_log or (not self.result and not self.is_killed): # ログパス生成 output_vmd_path = self.frame.file_panel_ctrl.file_set.output_vmd_file_ctrl.file_ctrl.GetPath() - output_log_path = re.sub(r'\.vmd$', '.log', output_vmd_path) + self.output_log_path = re.sub(r'\.vmd$', '.log', output_vmd_path) # 出力されたメッセージを全部出力 - self.frame.file_panel_ctrl.console_ctrl.SaveFile(filename=output_log_path) + self.frame.file_panel_ctrl.console_ctrl.SaveFile(filename=self.output_log_path) except Exception: pass - logging.shutdown() - def thread_delete(self): del self.options gc.collect() def post_event(self): - wx.PostEvent(self.frame, self.result_event(result=self.result and not self.is_killed, target_idx=self.target_idx, elapsed_time=self.elapsed_time)) + wx.PostEvent(self.frame, self.result_event(result=self.result and not self.is_killed, target_idx=self.target_idx, elapsed_time=self.elapsed_time, output_log_path=self.output_log_path)) diff --git a/src/form/worker/SmoothWorkerThread.py b/src/form/worker/SmoothWorkerThread.py index d12e1cf..038b2c6 100644 --- a/src/form/worker/SmoothWorkerThread.py +++ b/src/form/worker/SmoothWorkerThread.py @@ -41,6 +41,7 @@ def thread_event(self): output_path=self.frame.smooth_panel_ctrl.output_smooth_vmd_file_ctrl.file_ctrl.GetPath(), \ loop_cnt=self.frame.smooth_panel_ctrl.loop_cnt_ctrl.GetValue(), \ interpolation=self.frame.smooth_panel_ctrl.interpolation_ctrl.GetSelection(), \ + bone_list=self.frame.smooth_panel_ctrl.get_bone_list(), \ monitor=self.frame.smooth_panel_ctrl.console_ctrl, \ is_file=False, \ outout_datetime=logger.outout_datetime, \ diff --git a/src/form/worker/__init__.py b/src/form/worker/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/mmd/__init__.py b/src/mmd/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/module/MOptions.py b/src/module/MOptions.py index ad96f2e..d849ac6 100644 --- a/src/module/MOptions.py +++ b/src/module/MOptions.py @@ -18,7 +18,8 @@ class MOptions(): def __init__(self, version_name, logging_level, max_workers, data_set_list, arm_options, \ - camera_motion, camera_output_vmd_path, monitor, is_file, outout_datetime): + camera_motion, camera_output_vmd_path, monitor, is_file, outout_datetime, \ + total_process, now_process, total_process_ctrl, now_process_ctrl, tree_process_dict): self.version_name = version_name self.logging_level = logging_level self.max_workers = max_workers @@ -29,6 +30,11 @@ def __init__(self, version_name, logging_level, max_workers, data_set_list, arm_ self.monitor = monitor self.is_file = is_file self.outout_datetime = outout_datetime + self.total_process = total_process + self.now_process = now_process + self.total_process_ctrl = total_process_ctrl + self.now_process_ctrl = now_process_ctrl + self.tree_process_dict = tree_process_dict # 複数件のファイルセットの足IKの比率を再設定する def calc_leg_ratio(self): diff --git a/src/module/__init__.py b/src/module/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/service/ConvertSmoothService.py b/src/service/ConvertSmoothService.py index a064958..58468a9 100644 --- a/src/service/ConvertSmoothService.py +++ b/src/service/ConvertSmoothService.py @@ -35,6 +35,8 @@ def execute(self): vmd=os.path.basename(self.options.motion.path)) # noqa service_data_txt = "{service_data_txt} モデル: {model}({model_name})\n".format(service_data_txt=service_data_txt, model=os.path.basename(self.options.motion.path), model_name=self.options.model.name) # noqa + service_data_txt = "{service_data_txt} 対象ボーン: {bone_names}回\n".format(service_data_txt=service_data_txt, + bone_names=", ".join(self.options.bone_list)) # noqa service_data_txt = "{service_data_txt} 処理回数: {loop_cnt}回\n".format(service_data_txt=service_data_txt, loop_cnt=self.options.loop_cnt) # noqa service_data_txt = "{service_data_txt} 補間方法: {interpolation}\n".format(service_data_txt=service_data_txt, @@ -65,7 +67,7 @@ def convert_smooth(self): futures = [] with ThreadPoolExecutor(thread_name_prefix="prepare", max_workers=self.options.max_workers) as executor: for bone_name in self.options.motion.bones.keys(): - if bone_name in self.options.model.bones: + if bone_name in self.options.model.bones and bone_name in self.options.bone_list: if self.options.interpolation == 0 and len(self.options.motion.bones[bone_name].keys()) >= 2: # 線形補間の場合、そのまま全打ち futures.append(executor.submit(self.prepare_linear, bone_name)) @@ -96,7 +98,7 @@ def convert_smooth(self): futures = [] with ThreadPoolExecutor(thread_name_prefix="filter", max_workers=self.options.max_workers) as executor: for bone_name in self.options.motion.bones.keys(): - if bone_name in self.options.model.bones: + if bone_name in self.options.model.bones and bone_name in self.options.bone_list: futures.append(executor.submit(self.fitering, bone_name)) concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) @@ -109,7 +111,7 @@ def convert_smooth(self): futures = [] with ThreadPoolExecutor(thread_name_prefix="remove", max_workers=self.options.max_workers) as executor: for bone_name in self.options.motion.bones.keys(): - if bone_name in self.options.model.bones: + if bone_name in self.options.model.bones and bone_name in self.options.bone_list: futures.append(executor.submit(self.remove_unnecessary_bf, bone_name)) concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) @@ -217,7 +219,7 @@ def prepare_curve(self, bone_name: str): logger.info("【スムージング1回目】%s - 回転Y 終了", bone_name) rz_all_values = MBezierUtils.calc_value_from_catmullrom(bone_name, fnos, rz_values) - logger.info("【スムージング1回目】%s - 回転X 終了", bone_name) + logger.info("【スムージング1回目】%s - 回転Z 終了", bone_name) else: if len(fnos) > 0: rx_all_values = np.zeros(fnos[-1] + 1) diff --git a/src/service/ConvertVmdService.py b/src/service/ConvertVmdService.py index bac077b..6c5958d 100644 --- a/src/service/ConvertVmdService.py +++ b/src/service/ConvertVmdService.py @@ -153,7 +153,7 @@ def convert_vmd(self): model = PmxModel() model.name = "CSV Convert Model" - data_set = MOptionsDataSet(bone_motion, None, model, bone_fpath, False, False, [], None, None, []) + data_set = MOptionsDataSet(bone_motion, model, model, bone_fpath, False, False, [], None, 0, []) VmdWriter(data_set).write() @@ -213,7 +213,7 @@ def convert_vmd(self): model = PmxModel() model.name = "カメラ・照明" - data_set = MOptionsDataSet(camera_motion, None, model, camera_fpath, False, False, [], None, None, []) + data_set = MOptionsDataSet(camera_motion, model, model, camera_fpath, False, False, [], None, 0, []) VmdWriter(data_set).write() diff --git a/src/service/MorphBlendService.py b/src/service/MorphBlendService.py index fbad79e..4e87c98 100644 --- a/src/service/MorphBlendService.py +++ b/src/service/MorphBlendService.py @@ -149,7 +149,7 @@ def blend_morph(self): morph_total_cnt += 1 - data_set = MOptionsDataSet(bone_motion, None, self.options.model, blend_fpath, False, False, [], None, None, []) + data_set = MOptionsDataSet(bone_motion, self.options.model, self.options.model, blend_fpath, False, False, [], None, 0, []) VmdWriter(data_set).write() diff --git a/src/service/SizingService.py b/src/service/SizingService.py index 8ba3e33..8978b6d 100644 --- a/src/service/SizingService.py +++ b/src/service/SizingService.py @@ -39,8 +39,9 @@ def execute(self): trace_model=os.path.basename(data_set.org_model.path), model_name=data_set.org_model.name) # noqa service_data_txt = "{service_data_txt}  変換先モデル: {replace_model} ({model_name})\n".format(service_data_txt=service_data_txt, replace_model=os.path.basename(data_set.rep_model.path), model_name=data_set.rep_model.name) # noqa - service_data_txt = "{service_data_txt}  カメラ作成元モデル: {trace_model} ({model_name})({offset_y})\n".format(service_data_txt=service_data_txt, - trace_model=os.path.basename(data_set.camera_org_model.path), model_name=data_set.camera_org_model.name, offset_y=data_set.camera_offset_y) # noqa + if data_set.camera_org_model: + service_data_txt = "{service_data_txt}  カメラ作成元モデル: {trace_model} ({model_name})({offset_y})\n".format(service_data_txt=service_data_txt, + trace_model=os.path.basename(data_set.camera_org_model.path), model_name=data_set.camera_org_model.name, offset_y=data_set.camera_offset_y) # noqa service_data_txt = "{service_data_txt}  スタンス追加補正有無: {detail_stance_flg}\n".format(service_data_txt=service_data_txt, detail_stance_flg=data_set.detail_stance_flg) # noqa if data_set.detail_stance_flg: @@ -79,6 +80,11 @@ def execute(self): service_data_txt = "{service_data_txt}------------------------".format(service_data_txt=service_data_txt) # noqa + if self.options.total_process_ctrl: + self.options.total_process_ctrl.write(str(self.options.total_process)) + self.options.now_process_ctrl.write("0") + self.options.now_process_ctrl.write(str(self.options.now_process_ctrl)) + logger.info(service_data_txt, decoration=MLogger.DECORATION_BOX) for data_set_idx, data_set in enumerate(self.options.data_set_list): diff --git a/src/service/__init__.py b/src/service/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/service/parts/ArmAlignmentService.py b/src/service/parts/ArmAlignmentService.py index 9fbc65a..1a265eb 100644 --- a/src/service/parts/ArmAlignmentService.py +++ b/src/service/parts/ArmAlignmentService.py @@ -72,6 +72,12 @@ def execute(self): # 位置合わせ実行 self.execute_alignment(fnos, all_alignment_group_list, all_messages, bone_names) + if self.options.now_process_ctrl: + self.options.now_process += 1 + self.options.now_process_ctrl.write(str(self.options.now_process)) + + self.options.tree_process_dict["位置合わせ"] = True + return True except MKilledException as ke: raise ke diff --git a/src/service/parts/ArmAvoidanceService.py b/src/service/parts/ArmAvoidanceService.py index 43ee578..a3f5ec1 100644 --- a/src/service/parts/ArmAvoidanceService.py +++ b/src/service/parts/ArmAvoidanceService.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- # +import os import numpy as np import concurrent.futures from concurrent.futures import ThreadPoolExecutor @@ -87,6 +88,15 @@ def execute_avoidance_pool(self, data_set_idx: int, direction: str): # if not f.result(): # return False + data_set = self.options.data_set_list[data_set_idx] + + if self.options.now_process_ctrl and direction == "右": + self.options.now_process += 1 + self.options.now_process_ctrl.write(str(self.options.now_process)) + + proccess_key = "【No.{0}】{1}({2})".format(data_set_idx + 1, os.path.basename(data_set.motion.path), data_set.rep_model.name) + self.options.tree_process_dict[proccess_key]["接触回避"] = True + return True except MKilledException as ke: raise ke diff --git a/src/service/parts/CameraService.py b/src/service/parts/CameraService.py index 43f45a6..563a1f3 100644 --- a/src/service/parts/CameraService.py +++ b/src/service/parts/CameraService.py @@ -13,9 +13,6 @@ logger = MLogger(__name__, level=1) -# 床処理用INDEX -FLOOR_IDX = -1 - # 顔系ボーン名 HEAD_BONE_NAMES = ["頭頂実体", "頭", "首", "左目", "右目"] # 体幹ボーン名 @@ -69,6 +66,12 @@ def execute(self): left_data_set_idx, left_bone_name, right_data_set_idx, right_bone_name, top_data_set_idx, top_bone_name, bottom_data_set_idx, bottom_bone_name) prev_fno = fno + + if self.options.now_process_ctrl: + self.options.now_process += 1 + self.options.now_process_ctrl.write(str(self.options.now_process)) + + self.options.tree_process_dict["カメラ補正"] = True return True except MKilledException as ke: diff --git a/src/service/parts/MorphService.py b/src/service/parts/MorphService.py index 238fdf7..2553682 100644 --- a/src/service/parts/MorphService.py +++ b/src/service/parts/MorphService.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- # +import os import copy from mmd.VmdData import VmdMorphFrame @@ -26,7 +27,14 @@ def execute(self): logger.info("モーフ置換 【No.%s】", (data_set_idx + 1), decoration=MLogger.DECORATION_LINE) self.replace_morph(data_set_idx, data_set) - + + if self.options.now_process_ctrl: + self.options.now_process += 1 + self.options.now_process_ctrl.write(str(self.options.now_process)) + + proccess_key = "【No.{0}】{1}({2})".format(data_set_idx + 1, os.path.basename(data_set.motion.path), data_set.rep_model.name) + self.options.tree_process_dict[proccess_key]["スタンス追加補正"]["モーフ置換"] = True + return True # モーフ置換実行 diff --git a/src/service/parts/MoveService.py b/src/service/parts/MoveService.py index 62d1a27..e7113b3 100644 --- a/src/service/parts/MoveService.py +++ b/src/service/parts/MoveService.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- # +import os import logging # noqa import numpy as np import concurrent.futures @@ -43,6 +44,13 @@ def execute(self): if bone_name in data_set.motion.bones and bone_name in data_set.rep_model.bones and len(data_set.motion.bones[bone_name].keys()) > 0: futures.append(executor.submit(self.adjust_move, data_set_idx, bone_name)) + if self.options.now_process_ctrl: + self.options.now_process += 1 + self.options.now_process_ctrl.write(str(self.options.now_process)) + + proccess_key = "【No.{0}】{1}({2})".format(data_set_idx + 1, os.path.basename(data_set.motion.path), data_set.rep_model.name) + self.options.tree_process_dict[proccess_key]["移動縮尺補正"] = True + concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) for f in futures: diff --git a/src/service/parts/StanceService.py b/src/service/parts/StanceService.py index 14ce4a9..a7f9b5e 100644 --- a/src/service/parts/StanceService.py +++ b/src/service/parts/StanceService.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- # +import os import numpy as np import math import concurrent.futures @@ -82,13 +83,13 @@ def execute_pool(self, data_set_idx: int): # 肩補正 self.adjust_shoulder_stance(data_set_idx, data_set) + # 腕スタンス補正 + self.adjust_arm_stance(data_set_idx, data_set) + if data_set.twist_flg: # 捩り分散あり self.spread_twist(data_set_idx, data_set) - # 腕スタンス補正 - self.adjust_arm_stance(data_set_idx, data_set) - if data_set.detail_stance_flg: # センターY補正 if "センターY補正" in data_set.selected_stance_details: @@ -133,6 +134,13 @@ def spread_twist(self, data_set_idx: int, data_set: MOptionsDataSet): if not f.result(): return False + if self.options.now_process_ctrl: + self.options.now_process += 1 + self.options.now_process_ctrl.write(str(self.options.now_process)) + + proccess_key = "【No.{0}】{1}({2})".format(data_set_idx + 1, os.path.basename(data_set.motion.path), data_set.rep_model.name) + self.options.tree_process_dict[proccess_key]["捩り分散"] = True + return True # 捩り分散左右 @@ -776,6 +784,13 @@ def adjust_leg_ik_stance(self, data_set_idx: int, data_set: MOptionsDataSet): if not f.result(): return False + if self.options.now_process_ctrl: + self.options.now_process += 1 + self.options.now_process_ctrl.write(str(self.options.now_process)) + + proccess_key = "【No.{0}】{1}({2})".format(data_set_idx + 1, os.path.basename(data_set.motion.path), data_set.rep_model.name) + self.options.tree_process_dict[proccess_key]["スタンス追加補正"]["足IK補正"] = True + return True # 足IK補正 @@ -978,6 +993,13 @@ def adjust_toe_ik_stance(self, data_set_idx: int, data_set: MOptionsDataSet): if not f.result(): return False + if self.options.now_process_ctrl: + self.options.now_process += 1 + self.options.now_process_ctrl.write(str(self.options.now_process)) + + proccess_key = "【No.{0}】{1}({2})".format(data_set_idx + 1, os.path.basename(data_set.motion.path), data_set.rep_model.name) + self.options.tree_process_dict[proccess_key]["スタンス追加補正"]["つま先IK補正"] = True + return True # つま先IK補正 @@ -1167,6 +1189,13 @@ def adjust_toe_stance(self, data_set_idx: int, data_set: MOptionsDataSet): if not f.result(): return False + if self.options.now_process_ctrl: + self.options.now_process += 1 + self.options.now_process_ctrl.write(str(self.options.now_process)) + + proccess_key = "【No.{0}】{1}({2})".format(data_set_idx + 1, os.path.basename(data_set.motion.path), data_set.rep_model.name) + self.options.tree_process_dict[proccess_key]["スタンス追加補正"]["つま先補正"] = True + return True # つま先補正 @@ -1336,6 +1365,15 @@ def adjust_center_stance(self, data_set_idx: int, data_set: MOptionsDataSet): else: logger.info("センターXZ補正: 【No.%s】[%s]のボーン群が、作成元もしくは変換先のいずれかで足りないため、処理をスキップします。", (data_set_idx + 1), ", ".join(center_target_bones)) + if self.options.now_process_ctrl: + self.options.now_process += 1 + self.options.now_process_ctrl.write(str(self.options.now_process)) + + proccess_key = "【No.{0}】{1}({2})".format(data_set_idx + 1, os.path.basename(data_set.motion.path), data_set.rep_model.name) + self.options.tree_process_dict[proccess_key]["スタンス追加補正"]["センターXZ補正"] = True + + return True + # センターY補正 def adjust_center_arm_stance(self, data_set_idx: int, data_set: MOptionsDataSet): logger.info("センターY補正 【No.%s】", (data_set_idx + 1), decoration=MLogger.DECORATION_LINE) @@ -1412,6 +1450,15 @@ def adjust_center_arm_stance(self, data_set_idx: int, data_set: MOptionsDataSet) else: logger.info("センターY補正: 【No.%s】[%s]のボーン群が、作成元もしくは変換先のいずれかで足りないため、処理をスキップします。", (data_set_idx + 1), ", ".join(center_target_bones)) + if self.options.now_process_ctrl: + self.options.now_process += 1 + self.options.now_process_ctrl.write(str(self.options.now_process)) + + proccess_key = "【No.{0}】{1}({2})".format(data_set_idx + 1, os.path.basename(data_set.motion.path), data_set.rep_model.name) + self.options.tree_process_dict[proccess_key]["スタンス追加補正"]["センターY補正"] = True + + return True + # 足IKによるセンターオフセット値 def calc_center_offset_by_arm(self, bf: VmdBoneFrame, data_set_idx: int, data_set: MOptionsDataSet, \ org_center_links: BoneLinks, org_arm_links: BoneLinks, org_leg_links: BoneLinks, \ @@ -1857,6 +1904,15 @@ def adjust_upper_stance(self, data_set_idx: int, data_set: MOptionsDataSet): else: logger.info("上半身補正: 【No.%s】[%s]のボーン群が、作成元もしくは変換先のいずれかで足りないため、処理をスキップします。", (data_set_idx + 1), ", ".join(upper_target_bones)) + if self.options.now_process_ctrl: + self.options.now_process += 1 + self.options.now_process_ctrl.write(str(self.options.now_process)) + + proccess_key = "【No.{0}】{1}({2})".format(data_set_idx + 1, os.path.basename(data_set.motion.path), data_set.rep_model.name) + self.options.tree_process_dict[proccess_key]["スタンス追加補正"]["上半身補正"] = True + + return True + # 下半身補正 def adjust_lower_stance(self, data_set_idx: int, data_set: MOptionsDataSet): logger.info("下半身補正 【No.%s】", (data_set_idx + 1), decoration=MLogger.DECORATION_LINE) @@ -1970,6 +2026,15 @@ def adjust_lower_stance(self, data_set_idx: int, data_set: MOptionsDataSet): else: logger.info("下半身補正: 【No.%s】[%s]のボーン群が、作成元もしくは変換先のいずれかで足りないため、処理をスキップします。", (data_set_idx + 1), ", ".join(lower_target_bones)) + if self.options.now_process_ctrl: + self.options.now_process += 1 + self.options.now_process_ctrl.write(str(self.options.now_process)) + + proccess_key = "【No.{0}】{1}({2})".format(data_set_idx + 1, os.path.basename(data_set.motion.path), data_set.rep_model.name) + self.options.tree_process_dict[proccess_key]["スタンス追加補正"]["下半身補正"] = True + + return True + # 体幹スタンス補正 def calc_rotation_stance_trunk(self, bf: VmdBoneFrame, data_set_idx: int, data_set: MOptionsDataSet, \ org_from_links: BoneLinks, org_to_links: BoneLinks, org_arm_links: BoneLinks, \ @@ -2099,6 +2164,13 @@ def adjust_shoulder_stance(self, data_set_idx: int, data_set: MOptionsDataSet): if not f.result(): return False + if self.options.now_process_ctrl: + self.options.now_process += 1 + self.options.now_process_ctrl.write(str(self.options.now_process)) + + proccess_key = "【No.{0}】{1}({2})".format(data_set_idx + 1, os.path.basename(data_set.motion.path), data_set.rep_model.name) + self.options.tree_process_dict[proccess_key]["スタンス追加補正"]["肩補正"] = True + return True # 肩補正左右 @@ -2611,6 +2683,13 @@ def adjust_arm_stance(self, data_set_idx: int, data_set: MOptionsDataSet): if not f.result(): return False + if self.options.now_process_ctrl: + self.options.now_process += 1 + self.options.now_process_ctrl.write(str(self.options.now_process)) + + proccess_key = "【No.{0}】{1}({2})".format(data_set_idx + 1, os.path.basename(data_set.motion.path), data_set.rep_model.name) + self.options.tree_process_dict[proccess_key]["腕スタンス補正"] = True + return True def adjust_arm_stance_twist_pool(self, data_set_idx: int, bone_name: str): diff --git a/src/service/parts/__init__.py b/src/service/parts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/utils/__init__.py b/src/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vmdising_np64.spec b/vmdising_np64.spec index 6efcc44..7cb897d 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,7 +26,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β36_64bit', + name='VmdSizing_5.01_β37_64bit', debug=False, bootloader_ignore_signals=False, strip=False, From fd9c02738ed0008650ea9a58ccc450a2678e12d0 Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Sat, 26 Sep 2020 04:28:24 +0900 Subject: [PATCH 04/37] =?UTF-8?q?5.01=5F=CE=B238?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VmdSizing_5.01_β38 miumiu ・モーフ置換の進捗メッセージ辞書エラー修正 ・ファイル強制上書きフラグ除去 --- src/executor.py | 2 +- src/form/MainFrame.py | 14 +++++++++++++- src/form/panel/FilePanel.py | 2 +- src/service/parts/MorphService.py | 2 +- vmdising_np64.spec | 2 +- 5 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/executor.py b/src/executor.py index 9d7f838..273e023 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00_β37" +VERSION_NAME = "ver5.00_β38" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/form/MainFrame.py b/src/form/MainFrame.py index da36261..2a4ede8 100644 --- a/src/form/MainFrame.py +++ b/src/form/MainFrame.py @@ -424,7 +424,15 @@ def on_exec_result(self, event: wx.Event): return False self.elapsed_time += event.elapsed_time - logger.info("\n処理時間: %s", self.show_worked_time()) + worked_time = "\n処理時間: {0}".format(self.show_worked_time()) + logger.info(worked_time) + + if self.is_out_log and event.output_log_path and os.path.exists(event.output_log_path): + # ログ出力対象である場合、追記 + with open(event.output_log_path, mode='a', encoding='utf-8') as f: + f.write(worked_time) + + logger.debug("self.worker = None") # ワーカー終了 self.worker = None @@ -441,6 +449,10 @@ def on_exec_result(self, event: wx.Event): # 終了音を鳴らす self.sound_finish() + # ファイルタブのコンソール + if sys.stdout != self.file_panel_ctrl.console_ctrl: + sys.stdout = self.file_panel_ctrl.console_ctrl + # タブ移動可 self.release_tab() # フォーム有効化 diff --git a/src/form/panel/FilePanel.py b/src/form/panel/FilePanel.py index 3a94377..75dfd57 100644 --- a/src/form/panel/FilePanel.py +++ b/src/form/panel/FilePanel.py @@ -228,7 +228,7 @@ def on_exec(self, event: wx.Event): event.Skip(False) def set_output_vmd_path(self, event, is_force=False): - self.file_set.set_output_vmd_path(event, is_force) + self.file_set.set_output_vmd_path(event) # カメラ出力パスも一緒に変更する self.frame.camera_panel_ctrl.header_panel.set_output_vmd_path(event) diff --git a/src/service/parts/MorphService.py b/src/service/parts/MorphService.py index 2553682..deba0d5 100644 --- a/src/service/parts/MorphService.py +++ b/src/service/parts/MorphService.py @@ -33,7 +33,7 @@ def execute(self): self.options.now_process_ctrl.write(str(self.options.now_process)) proccess_key = "【No.{0}】{1}({2})".format(data_set_idx + 1, os.path.basename(data_set.motion.path), data_set.rep_model.name) - self.options.tree_process_dict[proccess_key]["スタンス追加補正"]["モーフ置換"] = True + self.options.tree_process_dict[proccess_key]["モーフ置換"] = True return True diff --git a/vmdising_np64.spec b/vmdising_np64.spec index 7cb897d..001f34e 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,7 +26,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β37_64bit', + name='VmdSizing_5.01_β38_64bit', debug=False, bootloader_ignore_signals=False, strip=False, From 57a6d0d5390d094e17086d6df7ed01e7dbbbc2ec Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Sat, 26 Sep 2020 05:03:51 +0900 Subject: [PATCH 05/37] =?UTF-8?q?5.01=5F=CE=B239?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VmdSizing_5.01_β39 miumiu ・数学ライブラリの拡張子をpyからpyx(cython専用)に置換 ・腕スタンス補正の進捗ダイアログ表示順を捩り分散の上に配置修正 --- src/executor.py | 2 +- src/form/worker/SizingWorkerThread.py | 4 ++-- src/module/MMath.pxd | 2 ++ src/module/{MMath.py => MMath.pyx} | 0 src/setup_ext.py | 2 +- vmdising_np64.spec | 2 +- 6 files changed, 7 insertions(+), 5 deletions(-) create mode 100644 src/module/MMath.pxd rename src/module/{MMath.py => MMath.pyx} (100%) diff --git a/src/executor.py b/src/executor.py index 273e023..fe357d9 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00_β38" +VERSION_NAME = "ver5.00_β39" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/form/worker/SizingWorkerThread.py b/src/form/worker/SizingWorkerThread.py index 43c9037..9fd97a8 100644 --- a/src/form/worker/SizingWorkerThread.py +++ b/src/form/worker/SizingWorkerThread.py @@ -72,12 +72,12 @@ def thread_event(self): for v in self.frame.file_panel_ctrl.file_set.get_selected_stance_details(): self.frame.file_panel_ctrl.tree_process_dict[proccess_key]["スタンス追加補正"][v] = False + self.frame.file_panel_ctrl.tree_process_dict[proccess_key]["腕スタンス補正"] = False + total_process += self.frame.file_panel_ctrl.file_set.rep_model_file_ctrl.title_parts_ctrl.GetValue() # 捩り分散 if self.frame.file_panel_ctrl.file_set.rep_model_file_ctrl.title_parts_ctrl.GetValue() == 1: self.frame.file_panel_ctrl.tree_process_dict[proccess_key]["捩り分散"] = False - self.frame.file_panel_ctrl.tree_process_dict[proccess_key]["腕スタンス補正"] = False - if self.frame.arm_panel_ctrl.arm_process_flg_avoidance.GetValue() > 0: self.frame.file_panel_ctrl.tree_process_dict[proccess_key]["接触回避"] = False diff --git a/src/module/MMath.pxd b/src/module/MMath.pxd new file mode 100644 index 0000000..ec51c5a --- /dev/null +++ b/src/module/MMath.pxd @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +# diff --git a/src/module/MMath.py b/src/module/MMath.pyx similarity index 100% rename from src/module/MMath.py rename to src/module/MMath.pyx diff --git a/src/setup_ext.py b/src/setup_ext.py index 5be5110..59b8b1f 100644 --- a/src/setup_ext.py +++ b/src/setup_ext.py @@ -8,7 +8,7 @@ def get_ext(): ext = [] - sources = ["module\\MMath.py", "module\\MParams.py"] + sources = ["module\\MMath.pyx", "module\\MParams.py"] # for path in glob.glob("*/**/*.pyx", recursive=True): # if os.path.isfile(path): # print(path) diff --git a/vmdising_np64.spec b/vmdising_np64.spec index 001f34e..95edab6 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,7 +26,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β38_64bit', + name='VmdSizing_5.01_β39_64bit', debug=False, bootloader_ignore_signals=False, strip=False, From 8e42fbe5f57e6bdd265cd9b3fd020fd0842f373d Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Sat, 26 Sep 2020 06:30:47 +0900 Subject: [PATCH 06/37] =?UTF-8?q?5.01=5F=CE=B240?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VmdSizing_5.01_β40 miumiu ・進捗数欄の数値チェック追加(エラーが出たら分かるように) ・数学ライブラリを必要最低限だけcython化 --- src/executor.py | 2 +- src/form/parts/StatusCtrl.py | 2 +- src/mmd/VmdData.py | 2 +- src/module/MMath.pxd | 88 ++++++ src/module/MMath.pyx | 566 +++++++++++++++++------------------ vmdising_np64.spec | 2 +- 6 files changed, 362 insertions(+), 300 deletions(-) diff --git a/src/executor.py b/src/executor.py index fe357d9..c4bda8a 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00_β39" +VERSION_NAME = "ver5.00_β40" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/form/parts/StatusCtrl.py b/src/form/parts/StatusCtrl.py index 262af24..b177998 100644 --- a/src/form/parts/StatusCtrl.py +++ b/src/form/parts/StatusCtrl.py @@ -14,7 +14,7 @@ def __init__(self, parent, id=wx.ID_ANY, value="", pos=wx.DefaultPosition, size= def write(self, text): try: - wx.CallAfter(self.SetValue, text) + wx.CallAfter(self.SetValue, str(int(text))) except: # noqa pass diff --git a/src/mmd/VmdData.py b/src/mmd/VmdData.py index 6f3c4ae..d822744 100644 --- a/src/mmd/VmdData.py +++ b/src/mmd/VmdData.py @@ -816,7 +816,7 @@ def get_bone_frames(self): target_fnos[bone_name] = self.get_bone_fnos(bone_name, is_key=True) for bone_name, fnos in target_fnos.items(): - logger.debug("%s, %s", bone_name, target_fnos[bone_name]) + logger.test("%s, %s", bone_name, target_fnos[bone_name]) if len(fnos) > 0: # 各ボーンの最終キーだけ先に登録 diff --git a/src/module/MMath.pxd b/src/module/MMath.pxd index ec51c5a..460d8a5 100644 --- a/src/module/MMath.pxd +++ b/src/module/MMath.pxd @@ -1,2 +1,90 @@ # -*- coding: utf-8 -*- # +import math +import numpy as np +cimport numpy as np +cimport libc.math as cmath +import quaternion # noqa + +from utils.MLogger import MLogger # noqa + +DTYPE_INT = np.int +ctypedef np.int_t DTYPE_INT_t + +DTYPE_INT8 = np.int8 +ctypedef np.int8_t DTYPE_INT8_t + +DTYPE_FLOAT = np.float64 +ctypedef np.float64_t DTYPE_FLOAT_t + +cdef class MRect: + cdef DTYPE_FLOAT_t __x + cdef DTYPE_FLOAT_t __y + cdef DTYPE_FLOAT_t __width + cdef DTYPE_FLOAT_t __height + +cdef class MVector2D: + cdef np.ndarray __data + + cpdef data(self) + + cpdef setX(self, x) + + cpdef setY(self, y) + + cpdef effective(self) + +cdef class MVector3D: + cdef np.ndarray __data + + cpdef data(self) + + cpdef setX(self, x) + + cpdef setY(self, y) + + cpdef setZ(self, z) + + cpdef effective(self) + +cdef class MVector4D: + cdef np.ndarray __data + + cpdef data(self) + + cpdef setX(self, x) + + cpdef setY(self, y) + + cpdef setZ(self, z) + + cpdef setW(self, w) + + cpdef effective(self) + +cdef class MQuaternion: + cdef np.ndarray __data + + cpdef data(self) + + cpdef setX(self, x) + + cpdef setY(self, y) + + cpdef setZ(self, z) + + cpdef setScalar(self, w) + +cdef class MMatrix4x4: + cdef np.ndarray __data + + cpdef data(self) + + cpdef rotate(self, qq) + + cpdef translate(self, vec3) + + cpdef scale(self, vec3) + + cpdef lookAt(self, eye, center, up) + \ No newline at end of file diff --git a/src/module/MMath.pyx b/src/module/MMath.pyx index 1372a5a..5d4d63c 100644 --- a/src/module/MMath.pyx +++ b/src/module/MMath.pyx @@ -3,13 +3,17 @@ import math import numpy as np import quaternion # noqa +cimport numpy as np +cimport cython +from libc.math cimport sin, cos, acos, atan2, asin, pi, sqrt +from math import degrees, radians from utils.MLogger import MLogger # noqa logger = MLogger(__name__) -class MRect(): +cdef class MRect: def __init__(self, x=0, y=0, width=0, height=0): self.__x = x @@ -33,12 +37,12 @@ class MRect(): return "MRect({0}, {1}, {2}, {3})".format(self.__x, self.__y, self.__width, self.__height) -class MVector2D(): +cdef class MVector2D: def __init__(self, x=0, y=0): if isinstance(x, MVector2D): # クラスの場合 - self.__data = x.__data + self.__data = x.data() elif isinstance(x, np.ndarray): # arrayそのものの場合 self.__data = np.array([x[0], x[1]], dtype=np.float64) @@ -46,180 +50,171 @@ class MVector2D(): self.__data = np.array([x, y], dtype=np.float64) def length(self): - return float(np.linalg.norm(self.__data, ord=2)) + return float(np.linalg.norm(self.data(), ord=2)) def lengthSquared(self): - return float(np.linalg.norm(self.__data, ord=2)**2) + return float(np.linalg.norm(self.data(), ord=2)**2) def normalized(self): - l2 = np.linalg.norm(self.__data, ord=2, axis=-1, keepdims=True) + l2 = np.linalg.norm(self.data(), ord=2, axis=-1, keepdims=True) l2[l2 == 0] = 1 - normv = self.__data / l2 + normv = self.data() / l2 return MVector2D(normv[0], normv[1]) def normalize(self): - l2 = np.linalg.norm(self.__data, ord=2, axis=-1, keepdims=True) + l2 = np.linalg.norm(self.data(), ord=2, axis=-1, keepdims=True) l2[l2 == 0] = 1 - normv = self.__data / l2 + normv = self.data() / l2 self.__data = normv - def effective(self): - self.__data[np.isnan(self.__data)] = 0 - self.__data[np.isinf(self.__data)] = 0 + cpdef effective(self): + self.__data[np.isnan(self.data())] = 0 + self.__data[np.isinf(self.data())] = 0 return self - def data(self): + cpdef data(self): return self.__data def __str__(self): - return "MVector2D({0}, {1})".format(self.__data[0], self.__data[1]) + return "MVector2D({0}, {1})".format(self.data()[0], self.data()[1]) def __lt__(self, other): - return np.all(self.__data < other.__data) + return np.all(self.data() < other.data()) def __le__(self, other): - return np.all(self.__data <= other.__data) + return np.all(self.data() <= other.data()) def __eq__(self, other): - return np.all(self.__data == other.__data) + return np.all(self.data() == other.data()) def __ne__(self, other): - return np.any(self.__data != other.__data) + return np.any(self.data() != other.data()) def __gt__(self, other): - return np.all(self.__data > other.__data) + return np.all(self.data() > other.data()) def __ge__(self, other): - return np.all(self.__data >= other.__data) + return np.all(self.data() >= other.data()) def __add__(self, other): if isinstance(other, MVector2D): - v = self.__data + other.__data + v = self.data() + other.data() else: - v = self.__data + other + v = self.data() + other v2 = self.__class__(v) v2.effective() return v2 def __sub__(self, other): if isinstance(other, MVector2D): - v = self.__data - other.__data + v = self.data() - other.data() else: - v = self.__data - other + v = self.data() - other v2 = self.__class__(v) v2.effective() return v2 def __mul__(self, other): if isinstance(other, MVector2D): - v = self.__data * other.__data + v = self.data() * other.data() else: - v = self.__data * other + v = self.data() * other v2 = self.__class__(v) v2.effective() return v2 def __truediv__(self, other): if isinstance(other, MVector2D): - v = self.__data / other.__data + v = self.data() / other.data() else: - v = self.__data / other + v = self.data() / other v2 = self.__class__(v) v2.effective() return v2 def __floordiv__(self, other): if isinstance(other, MVector2D): - v = self.__data // other.__data + v = self.data() // other.data() else: - v = self.__data // other + v = self.data() // other v2 = self.__class__(v) v2.effective() return v2 def __mod__(self, other): if isinstance(other, MVector2D): - v = self.__data % other.__data - else: - v = self.__data % other - v2 = self.__class__(v) - v2.effective() - return v2 - - def __pow__(self, other): - if isinstance(other, MVector2D): - v = self.__data ** other.__data + v = self.data() % other.data() else: - v = self.__data ** other + v = self.data() % other v2 = self.__class__(v) v2.effective() return v2 def __lshift__(self, other): if isinstance(other, MVector2D): - v = self.__data << other.__data + v = self.data() << other.data() else: - v = self.__data << other + v = self.data() << other v2 = self.__class__(v) v2.effective() return v2 def __rshift__(self, other): if isinstance(other, MVector2D): - v = self.__data >> other.__data + v = self.data() >> other.data() else: - v = self.__data >> other + v = self.data() >> other v2 = self.__class__(v) v2.effective() return v2 def __and__(self, other): - v = self.__data & other.__data + v = self.data() & other.data() v2 = self.__class__(v) v2.effective() return v2 def __dataor__(self, other): - v = self.__data ^ other.__data + v = self.data() ^ other.data() v2 = self.__class__(v) v2.effective() return v2 def __or__(self, other): - v = self.__data | other.__data + v = self.data() | other.data() v2 = self.__class__(v) v2.effective() return v2 def __neg__(self): - return self.__class__(-self.__data[0], -self.__data[1]) + return self.__class__(-self.data()[0], -self.data()[1]) def __pos__(self): - return self.__class__(+self.__data[0], +self.__data[1]) + return self.__class__(+self.data()[0], +self.data()[1]) def __invert__(self): - return self.__class__(~self.__data[0], ~self.__data[1]) + return self.__class__(~self.data()[0], ~self.data()[1]) def x(self): - return self.__data[0] + return self.data()[0] def y(self): - return self.__data[1] + return self.data()[1] - def setX(self, x): + cpdef setX(self, x): self.__data[0] = x - def setY(self, y): + cpdef setY(self, y): self.__data[1] = y -class MVector3D(): +cdef class MVector3D: def __init__(self, x=0.0, y=0.0, z=0.0): if isinstance(x, MVector3D): # クラスの場合 - self.__data = x.__data + self.__data = x.data() elif isinstance(x, np.ndarray): # arrayそのものの場合 self.__data = np.array([x[0], x[1], x[2]], dtype=np.float64) @@ -230,26 +225,26 @@ class MVector3D(): return MVector3D(self.x(), self.y(), self.z()) def length(self): - return np.linalg.norm(self.__data, ord=2) + return np.linalg.norm(self.data(), ord=2) def lengthSquared(self): - return np.linalg.norm(self.__data, ord=2)**2 + return np.linalg.norm(self.data(), ord=2)**2 def normalized(self): - l2 = np.linalg.norm(self.__data, ord=2, axis=-1, keepdims=True) + l2 = np.linalg.norm(self.data(), ord=2, axis=-1, keepdims=True) l2[l2 == 0] = 1 - normv = self.__data / l2 + normv = self.data() / l2 return MVector3D(normv[0], normv[1], normv[2]) def normalize(self): self.effective() - l2 = np.linalg.norm(self.__data, ord=2, axis=-1, keepdims=True) + l2 = np.linalg.norm(self.data(), ord=2, axis=-1, keepdims=True) l2[l2 == 0] = 1 - normv = self.__data / l2 + normv = self.data() / l2 self.__data = normv def distanceToPoint(self, v): - return MVector3D(self.__data - v.__data).length() + return MVector3D(self.data() - v.data()).length() def project(self, modelView, projection, viewport): tmp = MVector4D(self.x(), self.y(), self.z(), 1) @@ -285,14 +280,14 @@ class MVector3D(): return obj.toVector3D() def toVector4D(self): - return MVector4D(self.__data[0], self.__data[1], self.__data[2], 0) + return MVector4D(self.data()[0], self.data()[1], self.data()[2], 0) def is_almost_null(self): - return (is_almost_null(self.__data[0]) and is_almost_null(self.__data[1]) and is_almost_null(self.__data[2])) + return (is_almost_null(self.data()[0]) and is_almost_null(self.data()[1]) and is_almost_null(self.data()[2])) - def effective(self): - self.__data[np.isnan(self.__data)] = 0 - self.__data[np.isinf(self.__data)] = 0 + cpdef effective(self): + self.__data[np.isnan(self.data())] = 0 + self.__data[np.isinf(self.data())] = 0 return self @@ -325,174 +320,165 @@ class MVector3D(): @classmethod def crossProduct(cls, v1, v2): - crossv = np.cross(v1.__data, v2.__data) + crossv = np.cross(v1.data(), v2.data()) return MVector3D(crossv[0], crossv[1], crossv[2]) @classmethod def dotProduct(cls, v1, v2): - dotv = np.dot(v1.__data, v2.__data) + dotv = np.dot(v1.data(), v2.data()) return dotv - - def data(self): + + cpdef data(self): return self.__data - + def to_log(self): - return "x: {0}, y: {1} z: {2}".format(round(self.__data[0], 5), round(self.__data[1], 5), round(self.__data[2], 5)) + return "x: {0}, y: {1} z: {2}".format(round(self.data()[0], 5), round(self.data()[1], 5), round(self.data()[2], 5)) def __str__(self): - return "MVector3D({0}, {1}, {2})".format(self.__data[0], self.__data[1], self.__data[2]) + return "MVector3D({0}, {1}, {2})".format(self.data()[0], self.data()[1], self.data()[2]) def __lt__(self, other): - return np.all(self.__data < other.__data) + return np.all(self.data() < other.data()) def __le__(self, other): - return np.all(self.__data <= other.__data) + return np.all(self.data() <= other.data()) def __eq__(self, other): - return np.all(self.__data == other.__data) + return np.all(self.data() == other.data()) def __ne__(self, other): - return np.any(self.__data != other.__data) + return np.any(self.data() != other.data()) def __gt__(self, other): - return np.all(self.__data > other.__data) + return np.all(self.data() > other.data()) def __ge__(self, other): - return np.all(self.__data >= other.__data) + return np.all(self.data() >= other.data()) def __add__(self, other): if isinstance(other, MVector3D): - v = self.__data + other.__data + v = self.data() + other.data() else: - v = self.__data + other + v = self.data() + other v2 = self.__class__(v) v2.effective() return v2 def __sub__(self, other): if isinstance(other, MVector3D): - v = self.__data - other.__data + v = self.data() - other.data() else: - v = self.__data - other + v = self.data() - other v2 = self.__class__(v) v2.effective() return v2 def __mul__(self, other): if isinstance(other, MVector3D): - v = self.__data * other.__data + v = self.data() * other.data() else: - v = self.__data * other + v = self.data() * other v2 = self.__class__(v) v2.effective() return v2 def __truediv__(self, other): if isinstance(other, MVector3D): - v = self.__data / other.__data + v = self.data() / other.data() else: - v = self.__data / other + v = self.data() / other v2 = self.__class__(v) v2.effective() return v2 def __floordiv__(self, other): if isinstance(other, MVector3D): - v = self.__data // other.__data + v = self.data() // other.data() else: - v = self.__data // other + v = self.data() // other v2 = self.__class__(v) v2.effective() return v2 def __mod__(self, other): if isinstance(other, MVector3D): - v = self.__data % other.__data + v = self.data() % other.data() else: - v = self.__data % other - v2 = self.__class__(v) - v2.effective() - return v2 - - def __pow__(self, other): - if isinstance(other, MVector3D): - v = self.__data ** other.__data - else: - v = self.__data ** other + v = self.data() % other v2 = self.__class__(v) v2.effective() return v2 def __lshift__(self, other): if isinstance(other, MVector3D): - v = self.__data << other.__data + v = self.data() << other.data() else: - v = self.__data << other + v = self.data() << other v2 = self.__class__(v) v2.effective() return v2 def __rshift__(self, other): if isinstance(other, MVector3D): - v = self.__data >> other.__data + v = self.data() >> other.data() else: - v = self.__data >> other + v = self.data() >> other v2 = self.__class__(v) v2.effective() return v2 def __and__(self, other): - v = self.__data & other.__data + v = self.data() & other.data() v2 = self.__class__(v) v2.effective() return v2 def __dataor__(self, other): - v = self.__data ^ other.__data + v = self.data() ^ other.data() v2 = self.__class__(v) v2.effective() return v2 def __or__(self, other): - v = self.__data | other.__data + v = self.data() | other.data() v2 = self.__class__(v) v2.effective() return v2 def __neg__(self): - return self.__class__(-self.__data[0], -self.__data[1], -self.__data[2]) + return self.__class__(-self.data()[0], -self.data()[1], -self.data()[2]) def __pos__(self): - return self.__class__(+self.__data[0], +self.__data[1], +self.__data[2]) + return self.__class__(+self.data()[0], +self.data()[1], +self.data()[2]) def __invert__(self): - return self.__class__(~self.__data[0], ~self.__data[1], ~self.__data[2]) + return self.__class__(~self.data()[0], ~self.data()[1], ~self.data()[2]) def x(self): - return self.__data[0] + return self.data()[0] def y(self): - return self.__data[1] + return self.data()[1] def z(self): - return self.__data[2] + return self.data()[2] - def setX(self, x): + cpdef setX(self, x): self.__data[0] = x - def setY(self, y): + cpdef setY(self, y): self.__data[1] = y - def setZ(self, z): + cpdef setZ(self, z): self.__data[2] = z -class MVector4D(): +cdef class MVector4D: def __init__(self, x=0, y=0, z=0, w=0): if isinstance(x, MVector4D): # クラスの場合 - self.__data = x.__data + self.__data = x.data() elif isinstance(x, np.ndarray): # 行列そのものの場合 self.__data = np.array([x[0], x[1], x[2], x[3]], dtype=np.float64) @@ -500,237 +486,228 @@ class MVector4D(): self.__data = np.array([x, y, z, w], dtype=np.float64) def length(self): - return np.linalg.norm(self.__data, ord=2) + return np.linalg.norm(self.data(), ord=2) def lengthSquared(self): - return np.linalg.norm(self.__data, ord=2)**2 + return np.linalg.norm(self.data(), ord=2)**2 def normalized(self): - l2 = np.linalg.norm(self.__data, ord=2, axis=-1, keepdims=True) + l2 = np.linalg.norm(self.data(), ord=2, axis=-1, keepdims=True) l2[l2 == 0] = 1 - normv = self.__data / l2 + normv = self.data() / l2 return MVector4D(normv[0], normv[1], normv[2], normv[3]) def normalize(self): - l2 = np.linalg.norm(self.__data, ord=2, axis=-1, keepdims=True) + l2 = np.linalg.norm(self.data(), ord=2, axis=-1, keepdims=True) l2[l2 == 0] = 1 - normv = self.__data / l2 + normv = self.data() / l2 self.__data = normv def toVector3D(self): - return MVector3D(self.__data[0], self.__data[1], self.__data[2]) + return MVector3D(self.data()[0], self.data()[1], self.data()[2]) def is_almost_null(self): - return (is_almost_null(self.__data[0]) and is_almost_null(self.__data[1]) and is_almost_null(self.__data[2]) and is_almost_null(self.__data[3])) + return (is_almost_null(self.data()[0]) and is_almost_null(self.data()[1]) and is_almost_null(self.data()[2]) and is_almost_null(self.data()[3])) - def effective(self): - self.__data[np.isnan(self.__data)] = 0 - self.__data[np.isinf(self.__data)] = 0 + cpdef effective(self): + self.__data[np.isnan(self.data())] = 0 + self.__data[np.isinf(self.data())] = 0 @classmethod def dotProduct(cls, v1, v2): - dotv = np.dot(v1.__data, v2.__data) + dotv = np.dot(v1.data(), v2.data()) return dotv - def data(self): + cpdef data(self): return self.__data def __str__(self): - return "MVector4D({0}, {1}, {2}, {3})".format(self.__data[0], self.__data[1], self.__data[2], self.__data[3]) + return "MVector4D({0}, {1}, {2}, {3})".format(self.data()[0], self.data()[1], self.data()[2], self.data()[3]) def __lt__(self, other): - return np.all(self.__data < other.__data) + return np.all(self.data() < other.data()) def __le__(self, other): - return np.all(self.__data <= other.__data) + return np.all(self.data() <= other.data()) def __eq__(self, other): - return np.all(self.__data == other.__data) + return np.all(self.data() == other.data()) def __ne__(self, other): - return np.any(self.__data != other.__data) + return np.any(self.data() != other.data()) def __gt__(self, other): - return np.all(self.__data > other.__data) + return np.all(self.data() > other.data()) def __ge__(self, other): - return np.all(self.__data >= other.__data) + return np.all(self.data() >= other.data()) def __add__(self, other): if isinstance(other, MVector4D): - v = self.__data + other.__data + v = self.data() + other.data() else: - v = self.__data + other + v = self.data() + other v2 = self.__class__(v) v2.effective() return v2 def __sub__(self, other): if isinstance(other, MVector4D): - v = self.__data - other.__data + v = self.data() - other.data() else: - v = self.__data - other + v = self.data() - other v2 = self.__class__(v) v2.effective() return v2 def __mul__(self, other): if isinstance(other, MVector4D): - v = self.__data * other.__data + v = self.data() * other.data() else: - v = self.__data * other + v = self.data() * other v2 = self.__class__(v) v2.effective() return v2 def __truediv__(self, other): if isinstance(other, MVector4D): - v = self.__data / other.__data + v = self.data() / other.data() else: - v = self.__data / other + v = self.data() / other v2 = self.__class__(v) v2.effective() return v2 def __floordiv__(self, other): if isinstance(other, MVector4D): - v = self.__data // other.__data + v = self.data() // other.data() else: - v = self.__data // other + v = self.data() // other v2 = self.__class__(v) v2.effective() return v2 def __mod__(self, other): if isinstance(other, MVector4D): - v = self.__data % other.__data + v = self.data() % other.data() else: - v = self.__data % other - v2 = self.__class__(v) - v2.effective() - return v2 - - def __pow__(self, other): - if isinstance(other, MVector4D): - v = self.__data ** other.__data - else: - v = self.__data ** other + v = self.data() % other v2 = self.__class__(v) v2.effective() return v2 def __lshift__(self, other): if isinstance(other, MVector4D): - v = self.__data << other.__data + v = self.data() << other.data() else: - v = self.__data << other + v = self.data() << other v2 = self.__class__(v) v2.effective() return v2 def __rshift__(self, other): if isinstance(other, MVector4D): - v = self.__data >> other.__data + v = self.data() >> other.data() else: - v = self.__data >> other + v = self.data() >> other v2 = self.__class__(v) v2.effective() return v2 def __and__(self, other): - v = self.__data & other.__data + v = self.data() & other.data() v2 = self.__class__(v) v2.effective() return v2 def __dataor__(self, other): - v = self.__data ^ other.__data + v = self.data() ^ other.data() v2 = self.__class__(v) v2.effective() return v2 def __or__(self, other): - v = self.__data | other.__data + v = self.data() | other.data() v2 = self.__class__(v) v2.effective() return v2 def __neg__(self): - return self.__class__(-self.__data[0], -self.__data[1], -self.__data[2], -self.__data[3]) + return self.__class__(-self.data()[0], -self.data()[1], -self.data()[2], -self.data()[3]) def __pos__(self): - return self.__class__(+self.__data[0], +self.__data[1], +self.__data[2], +self.__data[3]) + return self.__class__(+self.data()[0], +self.data()[1], +self.data()[2], +self.data()[3]) def __invert__(self): - return self.__class__(~self.__data[0], ~self.__data[1], ~self.__data[2], ~self.__data[3]) + return self.__class__(~self.data()[0], ~self.data()[1], ~self.data()[2], ~self.data()[3]) def x(self): - return self.__data[0] + return self.data()[0] def y(self): - return self.__data[1] + return self.data()[1] def z(self): - return self.__data[2] + return self.data()[2] def w(self): - return self.__data[3] + return self.data()[3] - def setX(self, x): + cpdef setX(self, x): self.__data[0] = x - def setY(self, y): + cpdef setY(self, y): self.__data[1] = y - def setZ(self, z): + cpdef setZ(self, z): self.__data[2] = z - def setW(self, w): + cpdef setW(self, w): self.__data[3] = w -class MQuaternion(): +cdef class MQuaternion: def __init__(self, w=1, x=0, y=0, z=0): if isinstance(w, MQuaternion): # クラスの場合 - self.__data = w.__data + self.__data = w.data().components elif isinstance(w, np.quaternion): # quaternionの場合 - self.__data = w + self.__data = w.components elif isinstance(w, np.ndarray): # arrayそのものの場合 - self.__data = np.quaternion(w[0], w[1], w[2], w[3]) + self.__data = w else: - self.__data = np.quaternion(w, x, y, z) + self.__data = np.array([w, x, y, z], dtype=np.float64) def copy(self): return MQuaternion(self.scalar(), self.x(), self.y(), self.z()) def __str__(self): - return "MQuaternion({0}, {1}, {2}, {3})".format(self.__data.w, self.__data.x, self.__data.y, self.__data.z) + return "MQuaternion({0}, {1}, {2}, {3})".format(self.data().w, self.data().x, self.data().y, self.data().z) def inverted(self): - v = self.__data.inverse() + v = self.data().inverse() return self.__class__(v.w, v.x, v.y, v.z) def length(self): - return self.__data.abs() + return self.data().abs() def lengthSquared(self): - return self.__data.abs()**2 + return self.data().abs()**2 def normalized(self): self.effective() - v = self.__data.normalized() + v = self.data().normalized() return MQuaternion(v.w, v.x, v.y, v.z) def normalize(self): - self.__data = self.__data.normalized() + self.__data = self.data().normalized().components def effective(self): - self.__data.components[np.isnan(self.__data.components)] = 0 - self.__data.components[np.isinf(self.__data.components)] = 0 + self.data().components[np.isnan(self.data().components)] = 0 + self.data().components[np.isinf(self.data().components)] = 0 # Scalarは1がデフォルトとなる self.setScalar(1 if self.scalar() == 0 else self.scalar()) @@ -739,7 +716,7 @@ class MQuaternion(): m = mat.data() # q(w,x,y,z)から(x,y,z,w)に並べ替え. - q2 = np.array([self.__data.x, self.__data.y, self.__data.z, self.__data.w], dtype=np.float64) + q2 = np.array([self.data().x, self.data().y, self.data().z, self.data().w], dtype=np.float64) m[0, 0] = q2[3] * q2[3] + q2[0] * q2[0] - q2[1] * q2[1] - q2[2] * q2[2] m[0, 1] = 2.0 * q2[0] * q2[1] - 2.0 * q2[3] * q2[2] @@ -767,7 +744,7 @@ class MQuaternion(): return mat def toVector4D(self): - return MVector4D(self.__data.x, self.__data.y, self.__data.z, self.__data.w) + return MVector4D(self.data().x, self.data().y, self.data().z, self.data().w) def toEulerAngles4MMD(self): # MMDの表記に合わせたオイラー角 @@ -777,10 +754,10 @@ class MQuaternion(): # http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q37 def toEulerAngles(self): - xp = self.__data.x - yp = self.__data.y - zp = self.__data.z - wp = self.__data.w + xp = self.data().x + yp = self.data().y + zp = self.data().z + wp = self.data().w xx = xp * xp xy = xp * yp @@ -835,7 +812,7 @@ class MQuaternion(): @classmethod def dotProduct(cls, v1, v2): - dotv = np.sum(v1.__data.components * v2.__data.components) + dotv = np.sum(v1.data().components * v2.data().components) return dotv @classmethod @@ -879,7 +856,7 @@ class MQuaternion(): @classmethod def fromDirection(cls, direction, up): - if direction.is_almost_null(): + if direction.is_almost_null: return MQuaternion() zAxis = direction.normalized() @@ -1023,151 +1000,144 @@ class MQuaternion(): return q1 * factor1 + q2b * factor2 def x(self): - return self.__data.x + return self.data().x def y(self): - return self.__data.y + return self.data().y def z(self): - return self.__data.z + return self.data().z def scalar(self): - return self.__data.w + return self.data().w def vector(self): - return MVector3D(self.__data.x, self.__data.y, self.__data.z) + return MVector3D(self.data().x, self.data().y, self.data().z) - def setX(self, x): - self.__data.x = x + cpdef setX(self, x): + self.__data[1] = x - def setY(self, y): - self.__data.y = y + cpdef setY(self, y): + self.__data[2] = y - def setZ(self, z): - self.__data.z = z + cpdef setZ(self, z): + self.__data[3] = z - def setScalar(self, w): - self.__data.w = w - - def data(self): - return self.__data + cpdef setScalar(self, w): + self.__data[0] = w + + cpdef data(self): + return np.quaternion(self.__data[0], self.__data[1], self.__data[2], self.__data[3]) def __lt__(self, other): - return np.all(self.__data < other.__data) + return np.all(self.data() < other.data()) def __le__(self, other): - return np.all(self.__data <= other.__data) + return np.all(self.data() <= other.data()) def __eq__(self, other): - return np.all(self.__data == other.__data) + return np.all(self.data() == other.data()) def __ne__(self, other): - return np.any(self.__data != other.__data) + return np.any(self.data() != other.data()) def __gt__(self, other): - return np.all(self.__data > other.__data) + return np.all(self.data() > other.data()) def __ge__(self, other): - return np.all(self.__data >= other.__data) + return np.all(self.data() >= other.data()) def __add__(self, other): if isinstance(other, MQuaternion): - v = self.__data + other.__data + v = self.data() + other.data() else: - v = self.__data + other + v = self.data() + other return self.__class__(v.w, v.x, v.y, v.z) def __sub__(self, other): if isinstance(other, MQuaternion): - v = self.__data - other.__data + v = self.data() - other.data() else: - v = self.__data - other + v = self.data() - other return self.__class__(v.w, v.x, v.y, v.z) def __mul__(self, other): if isinstance(other, MQuaternion): - v = self.__data * other.__data + v = self.data() * other.data() return self.__class__(v) elif isinstance(other, MVector3D): v = self.toMatrix4x4() * other return v else: - v = self.__data * other + v = self.data() * other return self.__class__(v.w, v.x, v.y, v.z) def __truediv__(self, other): if isinstance(other, MQuaternion): - v = self.__data / other.__data + v = self.data() / other.data() else: - v = self.__data / other + v = self.data() / other return self.__class__(v.w, v.x, v.y, v.z) def __floordiv__(self, other): if isinstance(other, MQuaternion): - v = self.__data // other.__data + v = self.data() // other.data() else: - v = self.__data // other + v = self.data() // other return self.__class__(v.w, v.x, v.y, v.z) def __mod__(self, other): if isinstance(other, MQuaternion): - v = self.__data % other.__data - else: - v = self.__data % other - return self.__class__(v.w, v.x, v.y, v.z) - - def __pow__(self, other): - if isinstance(other, MQuaternion): - v = self.__data ** other.__data + v = self.data() % other.data() else: - v = self.__data ** other + v = self.data() % other return self.__class__(v.w, v.x, v.y, v.z) def __lshift__(self, other): if isinstance(other, MQuaternion): - v = self.__data << other.__data + v = self.data() << other.data() else: - v = self.__data << other + v = self.data() << other return self.__class__(v.w, v.x, v.y, v.z) def __rshift__(self, other): if isinstance(other, MQuaternion): - v = self.__data >> other.__data + v = self.data() >> other.data() else: - v = self.__data >> other + v = self.data() >> other return self.__class__(v.w, v.x, v.y, v.z) def __and__(self, other): - v = self.__data & other.__data + v = self.data() & other.data() return self.__class__(v.w, v.x, v.y, v.z) def __dataor__(self, other): - v = self.__data ^ other.__data + v = self.data() ^ other.data() return self.__class__(v.w, v.x, v.y, v.z) def __or__(self, other): - v = self.__data | other.__data + v = self.data() | other.data() return self.__class__(v.w, v.x, v.y, v.z) def __neg__(self): - return self.__class__(-self.__data.w, -self.__data.x, -self.__data.y, -self.__data.z) + return self.__class__(-self.data().w, -self.data().x, -self.data().y, -self.data().z) def __pos__(self): - return self.__class__(+self.__data.w, +self.__data.x, +self.__data.y, +self.__data.z) + return self.__class__(+self.data().w, +self.data().x, +self.data().y, +self.data().z) def __invert__(self): - return self.__class__(~self.__data.w, ~self.__data.x, ~self.__data.y, ~self.__data.z) + return self.__class__(~self.data().w, ~self.data().x, ~self.data().y, ~self.data().z) -class MMatrix4x4(): +cdef class MMatrix4x4: def __init__(self, m11=1, m12=0, m13=0, m14=0, m21=0, m22=1, m23=0, m24=0, m31=0, m32=0, m33=1, m34=0, m41=0, m42=0, m43=0, m44=1): if isinstance(m11, MMatrix4x4): # 行列クラスの場合 - self.__data = np.array([[m11.__data[0, 0], m11.__data[0, 1], m11.__data[0, 2], m11.__data[0, 3]], \ - [m11.__data[1, 0], m11.__data[1, 1], m11.__data[1, 2], m11.__data[1, 3]], \ - [m11.__data[2, 0], m11.__data[2, 1], m11.__data[2, 2], m11.__data[2, 3]], \ - [m11.__data[3, 0], m11.__data[3, 1], m11.__data[3, 2], m11.__data[3, 3]]], dtype=np.float64) + self.__data = np.array([[m11.data()[0, 0], m11.data()[0, 1], m11.data()[0, 2], m11.data()[0, 3]], \ + [m11.data()[1, 0], m11.data()[1, 1], m11.data()[1, 2], m11.data()[1, 3]], \ + [m11.data()[2, 0], m11.data()[2, 1], m11.data()[2, 2], m11.data()[2, 3]], \ + [m11.data()[3, 0], m11.data()[3, 1], m11.data()[3, 2], m11.data()[3, 3]]], dtype=np.float64) elif isinstance(m11, np.ndarray): # 行列そのものの場合 self.__data = np.array([[m11[0, 0], m11[0, 1], m11[0, 2], m11[0, 3]], [m11[1, 0], m11[1, 1], m11[1, 2], m11[1, 3]], \ @@ -1177,29 +1147,29 @@ class MMatrix4x4(): self.__data = np.array([[m11, m12, m13, m14], [m21, m22, m23, m24], [m31, m32, m33, m34], [m41, m42, m43, m44]], dtype=np.float64) def copy(self): - return MMatrix4x4(self.__data) - - def data(self): - return self.__data + return MMatrix4x4(self.data()) + cpdef data(self): + return self.__data + # 逆行列 def inverted(self): - v = np.linalg.inv(self.__data) + v = np.linalg.inv(self.data()) return MMatrix4x4(v) # 回転行列 - def rotate(self, qq): + cpdef rotate(self, qq): qq_mat = qq.toMatrix4x4() - self.__data = self.__data.dot(qq_mat.__data) + self.__data = self.data().dot(qq_mat.data()) # 平行移動行列 - def translate(self, vec3): + cpdef translate(self, vec3): vec_mat = np.tile(np.array([vec3.x(), vec3.y(), vec3.z()]), (4, 1)) data_mat = self.__data[:, :3] * vec_mat self.__data[:, 3] += np.sum(data_mat, axis=1) # 縮尺行列 - def scale(self, vec3): + cpdef scale(self, vec3): vec_mat = np.tile(np.array([vec3.x(), vec3.y(), vec3.z()]), (4, 1)) self.__data[:, :3] *= vec_mat @@ -1207,7 +1177,7 @@ class MMatrix4x4(): def setToIdentity(self): self.__data = np.eye(4) - def lookAt(self, eye, center, up): + cpdef lookAt(self, eye, center, up): forward = center - eye if forward.is_almost_null(): # ほぼ0の場合終了 @@ -1240,25 +1210,28 @@ class MMatrix4x4(): clip = farPlane - nearPlane m = MMatrix4x4() - m.__data[0, 0] = cotan / aspectRatio - m.__data[1, 1] = cotan - m.__data[2, 2] = -(nearPlane + farPlane) / clip - m.__data[2, 3] = -(2 * nearPlane * farPlane) / clip - m.__data[3, 2] = -1 + md = m.data() + md[0, 0] = cotan / aspectRatio + md[1, 1] = cotan + md[2, 2] = -(nearPlane + farPlane) / clip + md[2, 3] = -(2 * nearPlane * farPlane) / clip + md[3, 2] = -1 self *= m def mapVector(self, vector): vec_mat = np.array([vector.x(), vector.y(), vector.z()]) - xyz = np.sum(vec_mat * self.__data[:3, :3], axis=1) + d = self.data() + xyz = np.sum(vec_mat * d[:3, :3], axis=1) return MVector3D(xyz[0], xyz[1], xyz[2]) def toQuaternion(self): - a = [[self.__data[0, 0], self.__data[0, 1], self.__data[0, 2], self.__data[0, 3]], - [self.__data[1, 0], self.__data[1, 1], self.__data[1, 2], self.__data[1, 3]], - [self.__data[2, 0], self.__data[2, 1], self.__data[2, 2], self.__data[2, 3]], - [self.__data[3, 0], self.__data[3, 1], self.__data[3, 2], self.__data[3, 3]]] + d = self.data() + a = [[d[0, 0], d[0, 1], d[0, 2], d[0, 3]], + [d[1, 0], d[1, 1], d[1, 2], d[1, 3]], + [d[2, 0], d[2, 1], d[2, 2], d[2, 3]], + [d[3, 0], d[3, 1], d[3, 2], d[3, 3]]] q = MQuaternion() @@ -1294,45 +1267,46 @@ class MMatrix4x4(): return q def __str__(self): - return "MMatrix4x4({0})".format(self.__data) + return "MMatrix4x4({0})".format(self.data()) def __lt__(self, other): - return np.all(self.__data < other.__data) + return np.all(self.data() < other.data()) def __le__(self, other): - return np.all(self.__data <= other.__data) + return np.all(self.data() <= other.data()) def __eq__(self, other): - return np.all(self.__data == other.__data) + return np.all(self.data() == other.data()) def __ne__(self, other): - return np.any(self.__data != other.__data) + return np.any(self.data() != other.data()) def __gt__(self, other): - return np.all(self.__data > other.__data) + return np.all(self.data() > other.data()) def __ge__(self, other): - return np.all(self.__data >= other.__data) + return np.all(self.data() >= other.data()) def __add__(self, other): if isinstance(other, MMatrix4x4): - v = self.__data + other.__data + v = self.data() + other.data() else: - v = self.__data + other + v = self.data() + other return self.__class__(v) def __sub__(self, other): if isinstance(other, MMatrix4x4): - v = self.__data - other.__data + v = self.data() - other.data() else: - v = self.__data - other + v = self.data() - other return self.__class__(v) # *演算子 def __mul__(self, other): if isinstance(other, MVector3D): + d = self.data() vec_mat = np.tile(np.array([other.x(), other.y(), other.z()]), (4, 1)) - data_sum = np.sum(vec_mat * self.__data[:, :3], axis=1) + self.__data[:, 3] + data_sum = np.sum(vec_mat * d[:, :3], axis=1) + d[:, 3] x = data_sum[0] y = data_sum[1] @@ -1347,7 +1321,7 @@ class MMatrix4x4(): return MVector3D(x / w, y / w, z / w) elif isinstance(other, MVector4D): vec_mat = np.tile(np.array([other.x(), other.y(), other.z(), other.w()]), (4, 1)) - data_sum = np.sum(vec_mat * self.__data, axis=1) + data_sum = np.sum(vec_mat * self.data(), axis=1) x = data_sum[0] y = data_sum[1] @@ -1356,27 +1330,27 @@ class MMatrix4x4(): return MVector4D(x, y, z, w) elif isinstance(other, MMatrix4x4): - v = np.dot(self.__data, other.__data) + v = np.dot(self.data(), other.data()) return self.__class__(v) - v = self.__data * other + v = self.data() * other return self.__class__(v) def __iadd__(self, other): - self.__data = self.__data + other.__data.T + self.__data = self.data() + other.data().T return self def __isub__(self, other): - self.__data = self.__data + other.__data.T + self.__data = self.data() + other.data().T return self def __imul__(self, other): - self.__data = np.dot(self.__data, other.__data) + self.__data = np.dot(self.data(), other.data()) return self def __itruediv__(self, other): - self.__data = self.__data / other.__data.T + self.__data = self.data() / other.data().T return self diff --git a/vmdising_np64.spec b/vmdising_np64.spec index 95edab6..8230a82 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,7 +26,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β39_64bit', + name='VmdSizing_5.01_β40_64bit', debug=False, bootloader_ignore_signals=False, strip=False, From db4893262ae4c7054f8fc4efc6e3f9be7cc7aa4d Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Sat, 26 Sep 2020 06:45:20 +0900 Subject: [PATCH 07/37] =?UTF-8?q?5.01=5F=CE=B241?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VmdSizing_5.01_β41 miumiu ・処理終了音を別スレッド化(joinはしてない) --- src/executor.py | 2 +- src/form/MainFrame.py | 4 ++++ src/module/MOptions.py | 7 ++++++- vmdising_np64.spec | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/executor.py b/src/executor.py index c4bda8a..1ffa2c7 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00_β40" +VERSION_NAME = "ver5.00_β41" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/form/MainFrame.py b/src/form/MainFrame.py index 2a4ede8..7a310d5 100644 --- a/src/form/MainFrame.py +++ b/src/form/MainFrame.py @@ -3,6 +3,7 @@ import os import sys import wx +import threading from form.panel.FilePanel import FilePanel from form.panel.MorphPanel import MorphPanel @@ -461,6 +462,9 @@ def on_exec_result(self, event: wx.Event): self.file_panel_ctrl.gauge_ctrl.SetValue(0) def sound_finish(self): + threading.Thread(target=self.sound_finish_thread).start() + + def sound_finish_thread(self): # 終了音を鳴らす if os.name == "nt": # Windows diff --git a/src/module/MOptions.py b/src/module/MOptions.py index d849ac6..ef4f4ee 100644 --- a/src/module/MOptions.py +++ b/src/module/MOptions.py @@ -244,7 +244,12 @@ def parse(cls, version_name: str): monitor=sys.stdout, \ is_file=True, \ outout_datetime=logger.outout_datetime, \ - max_workers=1) + max_workers=1, \ + total_process=0, \ + now_process=0, \ + total_process_ctrl=None, \ + now_process_ctrl=None, \ + tree_process_dict={}) return options except SizingException as se: diff --git a/vmdising_np64.spec b/vmdising_np64.spec index 8230a82..1840d1a 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,7 +26,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β40_64bit', + name='VmdSizing_5.01_β41_64bit', debug=False, bootloader_ignore_signals=False, strip=False, From b8f1a10c768776915d9ac86e8e33f3e91d9d4973 Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Sat, 26 Sep 2020 17:31:38 +0900 Subject: [PATCH 08/37] =?UTF-8?q?5.01=5F=CE=B242?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VmdSizing_5.01_β42 miumiu ・VMD変換処理  ・エラーハンドリング追加 --- src/executor.py | 2 +- src/service/ConvertVmdService.py | 258 +++++++++++++++++++++++-------- vmdising_np64.spec | 2 +- 3 files changed, 197 insertions(+), 65 deletions(-) diff --git a/src/executor.py b/src/executor.py index 1ffa2c7..f93fdfe 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00_β41" +VERSION_NAME = "ver5.00_β42" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/service/ConvertVmdService.py b/src/service/ConvertVmdService.py index 6c5958d..606ce53 100644 --- a/src/service/ConvertVmdService.py +++ b/src/service/ConvertVmdService.py @@ -69,34 +69,79 @@ def convert_vmd(self): next(reader) # ヘッダーを読み飛ばす cnt = 0 - for row in reader: + for ridx, row in enumerate(reader): bf = VmdBoneFrame() - - # ボーン名 - bf.set_name(row[0]) - - # フレーム - bf.fno = int(float(row[1])) - - # 位置 - bf.position = MVector3D(float(row[2]), float(row[3]), float(row[4])) - - # 回転 - bf.rotation = MQuaternion.fromEulerAngles(float(row[5]), float(row[6]) * -1, float(row[7]) * -1) - - # 補間曲線 - # 補間曲線(一旦floatで読み込んで指数等も読み込んだ後、intに変換) - bf.interpolation = [int(float(row[8])), int(float(row[9])), int(float(row[10])), int(float(row[11])), int(float(row[12])), int(float(row[13])), \ - int(float(row[14])), int(float(row[15])), int(float(row[16])), int(float(row[17])), int(float(row[18])), int(float(row[19])), \ - int(float(row[20])), int(float(row[21])), int(float(row[22])), int(float(row[23])), int(float(row[24])), int(float(row[25])), \ - int(float(row[26])), int(float(row[27])), int(float(row[28])), int(float(row[29])), int(float(row[30])), int(float(row[31])), \ - int(float(row[32])), int(float(row[33])), int(float(row[34])), int(float(row[35])), int(float(row[36])), int(float(row[37])), \ - int(float(row[38])), int(float(row[39])), int(float(row[40])), int(float(row[41])), int(float(row[42])), int(float(row[43])), \ - int(float(row[44])), int(float(row[45])), int(float(row[46])), int(float(row[47])), int(float(row[48])), int(float(row[49])), \ - int(float(row[50])), int(float(row[51])), int(float(row[52])), int(float(row[53])), int(float(row[54])), int(float(row[55])), \ - int(float(row[56])), int(float(row[57])), int(float(row[58])), int(float(row[59])), int(float(row[60])), int(float(row[61])), \ - int(float(row[62])), int(float(row[63])), int(float(row[64])), int(float(row[65])), int(float(row[66])), int(float(row[67])), \ - int(float(row[68])), int(float(row[69])), int(float(row[70])), int(float(row[71]))] + rno = ridx + 1 + + try: + if len(row) < 0 or not row[0]: + logger.error("%s行目のボーン名(1列目)が設定されていません", rno, decoration=MLogger.DECORATION_BOX) + return False + + # ボーン名 + bf.set_name(row[0]) + except Exception as e: + logger.error("%s行目のボーン名の読み取りに失敗しました\n%s", rno, e, decoration=MLogger.DECORATION_BOX) + return False + + try: + if len(row) < 1 or not row[1]: + logger.error("%s行目のフレーム番号(2列目)が設定されていません", rno, decoration=MLogger.DECORATION_BOX) + return False + + # フレーム + bf.fno = int(float(row[1])) + except Exception as e: + logger.error("%s行目のフレーム番号の読み取りに失敗しました\nフレーム番号は半角数字のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) + return False + + try: + if len(row) < 4 or not row[2] or not row[3] or not row[4]: + logger.error("%s行目の位置(3-5列目)のいずれかが設定されていません", rno, decoration=MLogger.DECORATION_BOX) + return False + + # 位置 + bf.position = MVector3D(float(row[2]), float(row[3]), float(row[4])) + except Exception as e: + logger.error("%s行目の位置の読み取りに失敗しました\n位置は半角数字・符号・小数点のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) + return False + + try: + if len(row) < 7 or not row[5] or not row[6] or not row[7]: + logger.error("%s行目の回転(6-8列目)のいずれかが設定されていません", rno, decoration=MLogger.DECORATION_BOX) + return False + + # 回転 + bf.rotation = MQuaternion.fromEulerAngles(float(row[5]), float(row[6]) * -1, float(row[7]) * -1) + except Exception as e: + logger.error("%s行目の回転の読み取りに失敗しました\n位置は半角数字・符号・小数点のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) + return False + + try: + if len(row) < 71: + logger.error("%s行目の補間曲線(9-72列目)のいずれかが設定されていません", rno, decoration=MLogger.DECORATION_BOX) + return False + + for cidx in range(8, 72): + if not row[cidx]: + logger.error("%s行目の補間曲線の%s番目が設定されていません", rno, cidx - 7, decoration=MLogger.DECORATION_BOX) + return False + + # 補間曲線(一旦floatで読み込んで指数等も読み込んだ後、intに変換) + bf.interpolation = [int(float(row[8])), int(float(row[9])), int(float(row[10])), int(float(row[11])), int(float(row[12])), int(float(row[13])), \ + int(float(row[14])), int(float(row[15])), int(float(row[16])), int(float(row[17])), int(float(row[18])), int(float(row[19])), \ + int(float(row[20])), int(float(row[21])), int(float(row[22])), int(float(row[23])), int(float(row[24])), int(float(row[25])), \ + int(float(row[26])), int(float(row[27])), int(float(row[28])), int(float(row[29])), int(float(row[30])), int(float(row[31])), \ + int(float(row[32])), int(float(row[33])), int(float(row[34])), int(float(row[35])), int(float(row[36])), int(float(row[37])), \ + int(float(row[38])), int(float(row[39])), int(float(row[40])), int(float(row[41])), int(float(row[42])), int(float(row[43])), \ + int(float(row[44])), int(float(row[45])), int(float(row[46])), int(float(row[47])), int(float(row[48])), int(float(row[49])), \ + int(float(row[50])), int(float(row[51])), int(float(row[52])), int(float(row[53])), int(float(row[54])), int(float(row[55])), \ + int(float(row[56])), int(float(row[57])), int(float(row[58])), int(float(row[59])), int(float(row[60])), int(float(row[61])), \ + int(float(row[62])), int(float(row[63])), int(float(row[64])), int(float(row[65])), int(float(row[66])), int(float(row[67])), \ + int(float(row[68])), int(float(row[69])), int(float(row[70])), int(float(row[71]))] + except Exception as e: + logger.error("%s行目の補間曲線の読み取りに失敗しました\n位置は半角数字のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) + return False bf.read = True bf.key = True @@ -126,17 +171,42 @@ def convert_vmd(self): next(reader) # ヘッダーを読み飛ばす cnt = 0 - for row in reader: + for ridx, row in enumerate(reader): mf = VmdMorphFrame() - - # ボーン名 - mf.set_name(row[0]) - - # フレーム - mf.fno = int(float(row[1])) - - # 位置 - mf.ratio = float(row[2]) + rno = ridx + 1 + + try: + if len(row) < 0 or not row[0]: + logger.error("%s行目のモーフ名(1列目)が設定されていません", rno, decoration=MLogger.DECORATION_BOX) + return False + + # ボーン名 + mf.set_name(row[0]) + except Exception as e: + logger.error("%s行目のモーフ名の読み取りに失敗しました\n%s", rno, e, decoration=MLogger.DECORATION_BOX) + return False + + try: + if len(row) < 1 or not row[1]: + logger.error("%s行目のフレーム番号(2列目)が設定されていません", rno, decoration=MLogger.DECORATION_BOX) + return False + + # フレーム + mf.fno = int(float(row[1])) + except Exception as e: + logger.error("%s行目のフレーム番号の読み取りに失敗しました\nフレーム番号は半角数字のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) + return False + + try: + if len(row) < 1 or not row[2]: + logger.error("%s行目のモーフ値(3列目)が設定されていません", rno, decoration=MLogger.DECORATION_BOX) + return False + + # 値 + mf.ratio = float(row[2]) + except Exception as e: + logger.error("%s行目のモーフ値の読み取りに失敗しました\nフレーム番号は半角数字のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) + return False if mf.name not in bone_motion.morphs: bone_motion.morphs[mf.name] = {} @@ -174,33 +244,95 @@ def convert_vmd(self): next(reader) # ヘッダーを読み飛ばす cnt = 0 - for row in reader: + for ridx, row in enumerate(reader): cf = VmdCameraFrame() - - # フレーム - cf.fno = int(row[0]) - - # 位置 - cf.position = MVector3D(float(row[1]), float(row[2]), float(row[3])) - - # 回転(オイラー角) - cf.euler = MVector3D(float(row[4]), float(row[5]), float(row[6])) - - # 距離 - cf.length = -(float(row[7])) - - # 視野角 - cf.angle = int(row[8]) - - # パース - cf.perspective = int(row[9]) - - # 補間曲線 - cf.interpolation = [int(float(row[10])), int(float(row[11])), int(float(row[12])), int(float(row[13])), int(float(row[14])), int(float(row[15])), \ - int(float(row[16])), int(float(row[17])), int(float(row[18])), int(float(row[19])), int(float(row[20])), int(float(row[21])), \ - int(float(row[22])), int(float(row[23])), int(float(row[24])), int(float(row[25])), int(float(row[26])), int(float(row[27])), \ - int(float(row[28])), int(float(row[29])), int(float(row[30])), int(float(row[31])), int(float(row[32])), int(float(row[33]))] - + rno = ridx + 1 + + try: + if len(row) < 1 or not row[0]: + logger.error("%s行目のフレーム番号(1列目)が設定されていません", rno, decoration=MLogger.DECORATION_BOX) + return False + + # フレーム + cf.fno = int(row[0]) + except Exception as e: + logger.error("%s行目のフレーム番号の読み取りに失敗しました\nフレーム番号は半角数字のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) + return False + + try: + if len(row) < 3 or not row[1] or not row[2] or not row[3]: + logger.error("%s行目の位置(2-4列目)のいずれかが設定されていません", rno, decoration=MLogger.DECORATION_BOX) + return False + + # 位置 + cf.position = MVector3D(float(row[1]), float(row[2]), float(row[3])) + except Exception as e: + logger.error("%s行目の位置の読み取りに失敗しました\n位置は半角数字・符号・小数点のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) + return False + + try: + if len(row) < 6 or not row[4] or not row[5] or not row[6]: + logger.error("%s行目の回転(5-7列目)のいずれかが設定されていません", rno, decoration=MLogger.DECORATION_BOX) + return False + + # 回転(オイラー角) + cf.euler = MVector3D(float(row[4]), float(row[5]), float(row[6])) + except Exception as e: + logger.error("%s行目の回転の読み取りに失敗しました\n回転は半角数字・符号・小数点のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) + return False + + try: + if len(row) < 7 or not row[7]: + logger.error("%s行目の距離(8列目)が設定されていません", rno, decoration=MLogger.DECORATION_BOX) + return False + + # 距離 + cf.length = -(float(row[7])) + except Exception as e: + logger.error("%s行目の距離の読み取りに失敗しました\n距離は半角数字・符号・小数点のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) + return False + + try: + if len(row) < 8 or not row[8]: + logger.error("%s行目の視野角(9列目)が設定されていません", rno, decoration=MLogger.DECORATION_BOX) + return False + + # 視野角 + cf.angle = int(row[8]) + except Exception as e: + logger.error("%s行目の視野角の読み取りに失敗しました\n視野角は半角数字のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) + return False + + try: + if len(row) < 8 or not row[9]: + logger.error("%s行目のパース(10列目)が設定されていません", rno, decoration=MLogger.DECORATION_BOX) + return False + + # パース + cf.perspective = int(row[9]) + except Exception as e: + logger.error("%s行目のパースの読み取りに失敗しました\nパースは0, 1のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) + return False + + try: + if len(row) < 33: + logger.error("%s行目の補間曲線(11-34列目)のいずれかが設定されていません", rno, decoration=MLogger.DECORATION_BOX) + return False + + for cidx in range(10, 34): + if not row[cidx]: + logger.error("%s行目の補間曲線の%s番目が設定されていません", rno, cidx - 9, decoration=MLogger.DECORATION_BOX) + return False + + # 補間曲線(一旦floatで読み込んで指数等も読み込んだ後、intに変換) + cf.interpolation = [int(float(row[10])), int(float(row[11])), int(float(row[12])), int(float(row[13])), int(float(row[14])), int(float(row[15])), \ + int(float(row[16])), int(float(row[17])), int(float(row[18])), int(float(row[19])), int(float(row[20])), int(float(row[21])), \ + int(float(row[22])), int(float(row[23])), int(float(row[24])), int(float(row[25])), int(float(row[26])), int(float(row[27])), \ + int(float(row[28])), int(float(row[29])), int(float(row[30])), int(float(row[31])), int(float(row[32])), int(float(row[33]))] + except Exception as e: + logger.error("%s行目の補間曲線の読み取りに失敗しました\n位置は半角数字のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) + return False + camera_motion.cameras[cf.fno] = cf cnt += 1 diff --git a/vmdising_np64.spec b/vmdising_np64.spec index 1840d1a..e2f8a37 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,7 +26,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β41_64bit', + name='VmdSizing_5.01_β42_64bit', debug=False, bootloader_ignore_signals=False, strip=False, From 0e55f6afa16c278f278240e5fa8fbcdb2854c079 Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Sat, 26 Sep 2020 17:54:21 +0900 Subject: [PATCH 09/37] =?UTF-8?q?5.01=5F=CE=B243?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VmdSizing_5.01_β43 miumiu ・VMD変換処理  ・エラーハンドリングにキーフレ、視野角、補間曲線の負数チェック追加  ・モーフ大きさエラーメッセージ修正 --- src/executor.py | 2 +- src/service/ConvertVmdService.py | 40 +++++++++++++++++++++++++++++--- vmdising_np64.spec | 2 +- 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/executor.py b/src/executor.py index f93fdfe..1e31760 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00_β42" +VERSION_NAME = "ver5.00_β43" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/service/ConvertVmdService.py b/src/service/ConvertVmdService.py index 606ce53..b8dad85 100644 --- a/src/service/ConvertVmdService.py +++ b/src/service/ConvertVmdService.py @@ -91,6 +91,11 @@ def convert_vmd(self): # フレーム bf.fno = int(float(row[1])) + + if bf.fno < 0: + logger.error("%s行目のフレーム番号(2列目)に負数が設定されています", rno, decoration=MLogger.DECORATION_BOX) + return False + except Exception as e: logger.error("%s行目のフレーム番号の読み取りに失敗しました\nフレーム番号は半角数字のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) return False @@ -139,6 +144,12 @@ def convert_vmd(self): int(float(row[56])), int(float(row[57])), int(float(row[58])), int(float(row[59])), int(float(row[60])), int(float(row[61])), \ int(float(row[62])), int(float(row[63])), int(float(row[64])), int(float(row[65])), int(float(row[66])), int(float(row[67])), \ int(float(row[68])), int(float(row[69])), int(float(row[70])), int(float(row[71]))] + + for bidx, bi in enumerate(bf.interpolation): + if 0 > bi: + logger.error("%s行目の補間曲線(%s列目)に負数が設定されています", rno, bidx + 9, decoration=MLogger.DECORATION_BOX) + return False + except Exception as e: logger.error("%s行目の補間曲線の読み取りに失敗しました\n位置は半角数字のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) return False @@ -193,19 +204,23 @@ def convert_vmd(self): # フレーム mf.fno = int(float(row[1])) + + if mf.fno < 0: + logger.error("%s行目のフレーム番号(2列目)に負数が設定されています", rno, decoration=MLogger.DECORATION_BOX) + return False except Exception as e: logger.error("%s行目のフレーム番号の読み取りに失敗しました\nフレーム番号は半角数字のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) return False try: - if len(row) < 1 or not row[2]: - logger.error("%s行目のモーフ値(3列目)が設定されていません", rno, decoration=MLogger.DECORATION_BOX) + if len(row) < 2 or not row[2]: + logger.error("%s行目の大きさ(3列目)が設定されていません", rno, decoration=MLogger.DECORATION_BOX) return False # 値 mf.ratio = float(row[2]) except Exception as e: - logger.error("%s行目のモーフ値の読み取りに失敗しました\nフレーム番号は半角数字のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) + logger.error("%s行目の大きさの読み取りに失敗しました\n大きさは半角数字・符号・小数点のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) return False if mf.name not in bone_motion.morphs: @@ -255,6 +270,10 @@ def convert_vmd(self): # フレーム cf.fno = int(row[0]) + + if cf.fno < 0: + logger.error("%s行目のフレーム番号(1列目)に負数が設定されています", rno, decoration=MLogger.DECORATION_BOX) + return False except Exception as e: logger.error("%s行目のフレーム番号の読み取りに失敗しました\nフレーム番号は半角数字のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) return False @@ -299,6 +318,11 @@ def convert_vmd(self): # 視野角 cf.angle = int(row[8]) + + if cf.angle < 0: + logger.error("%s行目の視野角(9列目)に負数が設定されています", rno, decoration=MLogger.DECORATION_BOX) + return False + except Exception as e: logger.error("%s行目の視野角の読み取りに失敗しました\n視野角は半角数字のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) return False @@ -310,6 +334,10 @@ def convert_vmd(self): # パース cf.perspective = int(row[9]) + + if cf.perspective not in [0, 1]: + logger.error("%s行目のパース(10列目)に0, 1以外の値が設定されています", rno, decoration=MLogger.DECORATION_BOX) + return False except Exception as e: logger.error("%s行目のパースの読み取りに失敗しました\nパースは0, 1のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) return False @@ -329,6 +357,12 @@ def convert_vmd(self): int(float(row[16])), int(float(row[17])), int(float(row[18])), int(float(row[19])), int(float(row[20])), int(float(row[21])), \ int(float(row[22])), int(float(row[23])), int(float(row[24])), int(float(row[25])), int(float(row[26])), int(float(row[27])), \ int(float(row[28])), int(float(row[29])), int(float(row[30])), int(float(row[31])), int(float(row[32])), int(float(row[33]))] + + for cidx, ci in enumerate(cf.interpolation): + if 0 > ci: + logger.error("%s行目の補間曲線(%s列目)に負数が設定されています", rno, cidx + 11, decoration=MLogger.DECORATION_BOX) + return False + except Exception as e: logger.error("%s行目の補間曲線の読み取りに失敗しました\n位置は半角数字のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) return False diff --git a/vmdising_np64.spec b/vmdising_np64.spec index e2f8a37..76c058a 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,7 +26,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β42_64bit', + name='VmdSizing_5.01_β43_64bit', debug=False, bootloader_ignore_signals=False, strip=False, From c592f799e7ce6eb4687afcb77a9df3470c0229a2 Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Sat, 26 Sep 2020 18:27:02 +0900 Subject: [PATCH 10/37] =?UTF-8?q?5.01=5F=CE=B244?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VmdSizing_5.01_β44 miumiu ・VMD変換処理  ・エラーメッセージにファイル種別追加 ・数学ライブラリ  ・init関数をcython対応 --- src/executor.py | 2 +- src/module/MMath.pyx | 42 +++++++++++------ src/service/ConvertVmdService.py | 78 ++++++++++++++++---------------- vmdising_np64.spec | 2 +- 4 files changed, 68 insertions(+), 56 deletions(-) diff --git a/src/executor.py b/src/executor.py index 1e31760..6f25d42 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00_β43" +VERSION_NAME = "ver5.00_β44" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/module/MMath.pyx b/src/module/MMath.pyx index 5d4d63c..a2ac159 100644 --- a/src/module/MMath.pyx +++ b/src/module/MMath.pyx @@ -39,10 +39,13 @@ cdef class MRect: cdef class MVector2D: - def __init__(self, x=0, y=0): - if isinstance(x, MVector2D): + def __init__(self, x=0.0, y=0.0): + if isinstance(x, float): + # 実数の場合 + self.__data = np.array([x, y], dtype=np.float64) + elif isinstance(x, MVector2D): # クラスの場合 - self.__data = x.data() + self.__data = np.array([x.x(), x.y()], dtype=np.float64) elif isinstance(x, np.ndarray): # arrayそのものの場合 self.__data = np.array([x[0], x[1]], dtype=np.float64) @@ -212,9 +215,12 @@ cdef class MVector2D: cdef class MVector3D: def __init__(self, x=0.0, y=0.0, z=0.0): - if isinstance(x, MVector3D): + if isinstance(x, float): + # 実数の場合 + self.__data = np.array([x, y, z], dtype=np.float64) + elif isinstance(x, MVector3D): # クラスの場合 - self.__data = x.data() + self.__data = np.array([x.x(), x.y(), x.z()], dtype=np.float64) elif isinstance(x, np.ndarray): # arrayそのものの場合 self.__data = np.array([x[0], x[1], x[2]], dtype=np.float64) @@ -475,10 +481,12 @@ cdef class MVector3D: cdef class MVector4D: - def __init__(self, x=0, y=0, z=0, w=0): - if isinstance(x, MVector4D): + def __init__(self, x=0.0, y=0.0, z=0.0, w=0.0): + if isinstance(x, float): + self.__data = np.array([x, y, z, w], dtype=np.float64) + elif isinstance(x, MVector4D): # クラスの場合 - self.__data = x.data() + self.__data = np.array([x.x(), x.y(), x.z(), x.w()], dtype=np.float64) elif isinstance(x, np.ndarray): # 行列そのものの場合 self.__data = np.array([x[0], x[1], x[2], x[3]], dtype=np.float64) @@ -668,16 +676,18 @@ cdef class MVector4D: cdef class MQuaternion: - def __init__(self, w=1, x=0, y=0, z=0): - if isinstance(w, MQuaternion): + def __init__(self, w=1.0, x=0.0, y=0.0, z=0.0): + if isinstance(w, float): + self.__data = np.array([w, x, y, z], dtype=np.float64) + elif isinstance(w, MQuaternion): # クラスの場合 - self.__data = w.data().components + self.__data = np.array([w.data().components.w, w.data().components.x, w.data().components.y, w.data().components.z], dtype=np.float64) elif isinstance(w, np.quaternion): # quaternionの場合 self.__data = w.components elif isinstance(w, np.ndarray): # arrayそのものの場合 - self.__data = w + self.__data = np.array([w[0], w[1], w[2], w[3]], dtype=np.float64) else: self.__data = np.array([w, x, y, z], dtype=np.float64) @@ -685,7 +695,7 @@ cdef class MQuaternion: return MQuaternion(self.scalar(), self.x(), self.y(), self.z()) def __str__(self): - return "MQuaternion({0}, {1}, {2}, {3})".format(self.data().w, self.data().x, self.data().y, self.data().z) + return "MQuaternion({0}, {1}, {2}, {3})".format(self.scalar(), self.x(), self.y(), self.z()) def inverted(self): v = self.data().inverse() @@ -1131,8 +1141,10 @@ cdef class MQuaternion: cdef class MMatrix4x4: - def __init__(self, m11=1, m12=0, m13=0, m14=0, m21=0, m22=1, m23=0, m24=0, m31=0, m32=0, m33=1, m34=0, m41=0, m42=0, m43=0, m44=1): - if isinstance(m11, MMatrix4x4): + def __init__(self, m11=1.0, m12=0.0, m13=0.0, m14=0.0, m21=0.0, m22=1.0, m23=0.0, m24=0.0, m31=0.0, m32=0.0, m33=1.0, m34=0.0, m41=0.0, m42=0.0, m43=0.0, m44=1.0): + if isinstance(m11, float): + self.__data = np.array([[m11, m12, m13, m14], [m21, m22, m23, m24], [m31, m32, m33, m34], [m41, m42, m43, m44]], dtype=np.float64) + elif isinstance(m11, MMatrix4x4): # 行列クラスの場合 self.__data = np.array([[m11.data()[0, 0], m11.data()[0, 1], m11.data()[0, 2], m11.data()[0, 3]], \ [m11.data()[1, 0], m11.data()[1, 1], m11.data()[1, 2], m11.data()[1, 3]], \ diff --git a/src/service/ConvertVmdService.py b/src/service/ConvertVmdService.py index b8dad85..c2e384d 100644 --- a/src/service/ConvertVmdService.py +++ b/src/service/ConvertVmdService.py @@ -75,61 +75,61 @@ def convert_vmd(self): try: if len(row) < 0 or not row[0]: - logger.error("%s行目のボーン名(1列目)が設定されていません", rno, decoration=MLogger.DECORATION_BOX) + logger.error("[ボーン] %s行目のボーン名(1列目)が設定されていません", rno, decoration=MLogger.DECORATION_BOX) return False # ボーン名 bf.set_name(row[0]) except Exception as e: - logger.error("%s行目のボーン名の読み取りに失敗しました\n%s", rno, e, decoration=MLogger.DECORATION_BOX) + logger.error("[ボーン] %s行目のボーン名の読み取りに失敗しました\n%s", rno, e, decoration=MLogger.DECORATION_BOX) return False try: if len(row) < 1 or not row[1]: - logger.error("%s行目のフレーム番号(2列目)が設定されていません", rno, decoration=MLogger.DECORATION_BOX) + logger.error("[ボーン] %s行目のフレーム番号(2列目)が設定されていません", rno, decoration=MLogger.DECORATION_BOX) return False # フレーム bf.fno = int(float(row[1])) if bf.fno < 0: - logger.error("%s行目のフレーム番号(2列目)に負数が設定されています", rno, decoration=MLogger.DECORATION_BOX) + logger.error("[ボーン] %s行目のフレーム番号(2列目)に負数が設定されています", rno, decoration=MLogger.DECORATION_BOX) return False except Exception as e: - logger.error("%s行目のフレーム番号の読み取りに失敗しました\nフレーム番号は半角数字のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) + logger.error("[ボーン] %s行目のフレーム番号の読み取りに失敗しました\nフレーム番号は半角数字のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) return False try: if len(row) < 4 or not row[2] or not row[3] or not row[4]: - logger.error("%s行目の位置(3-5列目)のいずれかが設定されていません", rno, decoration=MLogger.DECORATION_BOX) + logger.error("[ボーン] %s行目の位置(3-5列目)のいずれかが設定されていません", rno, decoration=MLogger.DECORATION_BOX) return False # 位置 bf.position = MVector3D(float(row[2]), float(row[3]), float(row[4])) except Exception as e: - logger.error("%s行目の位置の読み取りに失敗しました\n位置は半角数字・符号・小数点のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) + logger.error("[ボーン] %s行目の位置の読み取りに失敗しました\n位置は半角数字・符号・小数点のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) return False try: if len(row) < 7 or not row[5] or not row[6] or not row[7]: - logger.error("%s行目の回転(6-8列目)のいずれかが設定されていません", rno, decoration=MLogger.DECORATION_BOX) + logger.error("[ボーン] %s行目の回転(6-8列目)のいずれかが設定されていません", rno, decoration=MLogger.DECORATION_BOX) return False # 回転 bf.rotation = MQuaternion.fromEulerAngles(float(row[5]), float(row[6]) * -1, float(row[7]) * -1) except Exception as e: - logger.error("%s行目の回転の読み取りに失敗しました\n位置は半角数字・符号・小数点のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) + logger.error("[ボーン] %s行目の回転の読み取りに失敗しました\n位置は半角数字・符号・小数点のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) return False try: if len(row) < 71: - logger.error("%s行目の補間曲線(9-72列目)のいずれかが設定されていません", rno, decoration=MLogger.DECORATION_BOX) + logger.error("[ボーン] %s行目の補間曲線(9-72列目)のいずれかが設定されていません", rno, decoration=MLogger.DECORATION_BOX) return False for cidx in range(8, 72): if not row[cidx]: - logger.error("%s行目の補間曲線の%s番目が設定されていません", rno, cidx - 7, decoration=MLogger.DECORATION_BOX) + logger.error("[ボーン] %s行目の補間曲線の%s番目が設定されていません", rno, cidx - 7, decoration=MLogger.DECORATION_BOX) return False # 補間曲線(一旦floatで読み込んで指数等も読み込んだ後、intに変換) @@ -147,11 +147,11 @@ def convert_vmd(self): for bidx, bi in enumerate(bf.interpolation): if 0 > bi: - logger.error("%s行目の補間曲線(%s列目)に負数が設定されています", rno, bidx + 9, decoration=MLogger.DECORATION_BOX) + logger.error("[ボーン] %s行目の補間曲線(%s列目)に負数が設定されています", rno, bidx + 9, decoration=MLogger.DECORATION_BOX) return False except Exception as e: - logger.error("%s行目の補間曲線の読み取りに失敗しました\n位置は半角数字のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) + logger.error("[ボーン] %s行目の補間曲線の読み取りに失敗しました\n位置は半角数字のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) return False bf.read = True @@ -188,39 +188,39 @@ def convert_vmd(self): try: if len(row) < 0 or not row[0]: - logger.error("%s行目のモーフ名(1列目)が設定されていません", rno, decoration=MLogger.DECORATION_BOX) + logger.error("[モーフ] %s行目のモーフ名(1列目)が設定されていません", rno, decoration=MLogger.DECORATION_BOX) return False # ボーン名 mf.set_name(row[0]) except Exception as e: - logger.error("%s行目のモーフ名の読み取りに失敗しました\n%s", rno, e, decoration=MLogger.DECORATION_BOX) + logger.error("[モーフ] %s行目のモーフ名の読み取りに失敗しました\n%s", rno, e, decoration=MLogger.DECORATION_BOX) return False try: if len(row) < 1 or not row[1]: - logger.error("%s行目のフレーム番号(2列目)が設定されていません", rno, decoration=MLogger.DECORATION_BOX) + logger.error("[モーフ] %s行目のフレーム番号(2列目)が設定されていません", rno, decoration=MLogger.DECORATION_BOX) return False # フレーム mf.fno = int(float(row[1])) if mf.fno < 0: - logger.error("%s行目のフレーム番号(2列目)に負数が設定されています", rno, decoration=MLogger.DECORATION_BOX) + logger.error("[モーフ] %s行目のフレーム番号(2列目)に負数が設定されています", rno, decoration=MLogger.DECORATION_BOX) return False except Exception as e: - logger.error("%s行目のフレーム番号の読み取りに失敗しました\nフレーム番号は半角数字のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) + logger.error("[モーフ] %s行目のフレーム番号の読み取りに失敗しました\nフレーム番号は半角数字のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) return False try: if len(row) < 2 or not row[2]: - logger.error("%s行目の大きさ(3列目)が設定されていません", rno, decoration=MLogger.DECORATION_BOX) + logger.error("[モーフ] %s行目の大きさ(3列目)が設定されていません", rno, decoration=MLogger.DECORATION_BOX) return False # 値 mf.ratio = float(row[2]) except Exception as e: - logger.error("%s行目の大きさの読み取りに失敗しました\n大きさは半角数字・符号・小数点のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) + logger.error("[モーフ] %s行目の大きさの読み取りに失敗しました\n大きさは半角数字・符号・小数点のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) return False if mf.name not in bone_motion.morphs: @@ -265,91 +265,91 @@ def convert_vmd(self): try: if len(row) < 1 or not row[0]: - logger.error("%s行目のフレーム番号(1列目)が設定されていません", rno, decoration=MLogger.DECORATION_BOX) + logger.error("[カメラ] %s行目のフレーム番号(1列目)が設定されていません", rno, decoration=MLogger.DECORATION_BOX) return False # フレーム cf.fno = int(row[0]) if cf.fno < 0: - logger.error("%s行目のフレーム番号(1列目)に負数が設定されています", rno, decoration=MLogger.DECORATION_BOX) + logger.error("[カメラ] %s行目のフレーム番号(1列目)に負数が設定されています", rno, decoration=MLogger.DECORATION_BOX) return False except Exception as e: - logger.error("%s行目のフレーム番号の読み取りに失敗しました\nフレーム番号は半角数字のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) + logger.error("[カメラ] %s行目のフレーム番号の読み取りに失敗しました\nフレーム番号は半角数字のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) return False try: if len(row) < 3 or not row[1] or not row[2] or not row[3]: - logger.error("%s行目の位置(2-4列目)のいずれかが設定されていません", rno, decoration=MLogger.DECORATION_BOX) + logger.error("[カメラ] %s行目の位置(2-4列目)のいずれかが設定されていません", rno, decoration=MLogger.DECORATION_BOX) return False # 位置 cf.position = MVector3D(float(row[1]), float(row[2]), float(row[3])) except Exception as e: - logger.error("%s行目の位置の読み取りに失敗しました\n位置は半角数字・符号・小数点のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) + logger.error("[カメラ] %s行目の位置の読み取りに失敗しました\n位置は半角数字・符号・小数点のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) return False try: if len(row) < 6 or not row[4] or not row[5] or not row[6]: - logger.error("%s行目の回転(5-7列目)のいずれかが設定されていません", rno, decoration=MLogger.DECORATION_BOX) + logger.error("[カメラ] %s行目の回転(5-7列目)のいずれかが設定されていません", rno, decoration=MLogger.DECORATION_BOX) return False # 回転(オイラー角) cf.euler = MVector3D(float(row[4]), float(row[5]), float(row[6])) except Exception as e: - logger.error("%s行目の回転の読み取りに失敗しました\n回転は半角数字・符号・小数点のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) + logger.error("[カメラ] %s行目の回転の読み取りに失敗しました\n回転は半角数字・符号・小数点のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) return False try: if len(row) < 7 or not row[7]: - logger.error("%s行目の距離(8列目)が設定されていません", rno, decoration=MLogger.DECORATION_BOX) + logger.error("[カメラ] %s行目の距離(8列目)が設定されていません", rno, decoration=MLogger.DECORATION_BOX) return False # 距離 cf.length = -(float(row[7])) except Exception as e: - logger.error("%s行目の距離の読み取りに失敗しました\n距離は半角数字・符号・小数点のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) + logger.error("[カメラ] %s行目の距離の読み取りに失敗しました\n距離は半角数字・符号・小数点のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) return False try: if len(row) < 8 or not row[8]: - logger.error("%s行目の視野角(9列目)が設定されていません", rno, decoration=MLogger.DECORATION_BOX) + logger.error("[カメラ] %s行目の視野角(9列目)が設定されていません", rno, decoration=MLogger.DECORATION_BOX) return False # 視野角 cf.angle = int(row[8]) if cf.angle < 0: - logger.error("%s行目の視野角(9列目)に負数が設定されています", rno, decoration=MLogger.DECORATION_BOX) + logger.error("[カメラ] %s行目の視野角(9列目)に負数が設定されています", rno, decoration=MLogger.DECORATION_BOX) return False except Exception as e: - logger.error("%s行目の視野角の読み取りに失敗しました\n視野角は半角数字のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) + logger.error("[カメラ] %s行目の視野角の読み取りに失敗しました\n視野角は半角数字のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) return False try: if len(row) < 8 or not row[9]: - logger.error("%s行目のパース(10列目)が設定されていません", rno, decoration=MLogger.DECORATION_BOX) + logger.error("[カメラ] %s行目のパース(10列目)が設定されていません", rno, decoration=MLogger.DECORATION_BOX) return False # パース cf.perspective = int(row[9]) if cf.perspective not in [0, 1]: - logger.error("%s行目のパース(10列目)に0, 1以外の値が設定されています", rno, decoration=MLogger.DECORATION_BOX) + logger.error("[カメラ] %s行目のパース(10列目)に0, 1以外の値が設定されています", rno, decoration=MLogger.DECORATION_BOX) return False except Exception as e: - logger.error("%s行目のパースの読み取りに失敗しました\nパースは0, 1のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) + logger.error("[カメラ] %s行目のパースの読み取りに失敗しました\nパースは0, 1のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) return False try: if len(row) < 33: - logger.error("%s行目の補間曲線(11-34列目)のいずれかが設定されていません", rno, decoration=MLogger.DECORATION_BOX) + logger.error("[カメラ] %s行目の補間曲線(11-34列目)のいずれかが設定されていません", rno, decoration=MLogger.DECORATION_BOX) return False for cidx in range(10, 34): if not row[cidx]: - logger.error("%s行目の補間曲線の%s番目が設定されていません", rno, cidx - 9, decoration=MLogger.DECORATION_BOX) + logger.error("[カメラ] %s行目の補間曲線の%s番目が設定されていません", rno, cidx - 9, decoration=MLogger.DECORATION_BOX) return False # 補間曲線(一旦floatで読み込んで指数等も読み込んだ後、intに変換) @@ -360,11 +360,11 @@ def convert_vmd(self): for cidx, ci in enumerate(cf.interpolation): if 0 > ci: - logger.error("%s行目の補間曲線(%s列目)に負数が設定されています", rno, cidx + 11, decoration=MLogger.DECORATION_BOX) + logger.error("[カメラ] %s行目の補間曲線(%s列目)に負数が設定されています", rno, cidx + 11, decoration=MLogger.DECORATION_BOX) return False except Exception as e: - logger.error("%s行目の補間曲線の読み取りに失敗しました\n位置は半角数字のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) + logger.error("[カメラ] %s行目の補間曲線の読み取りに失敗しました\n位置は半角数字のみ入力可能です。\n%s", rno, e, decoration=MLogger.DECORATION_BOX) return False camera_motion.cameras[cf.fno] = cf diff --git a/vmdising_np64.spec b/vmdising_np64.spec index 76c058a..6c09a77 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,7 +26,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β43_64bit', + name='VmdSizing_5.01_β44_64bit', debug=False, bootloader_ignore_signals=False, strip=False, From a99437e4bb8583860b8c29ce38da2e0d5d11938d Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Sat, 26 Sep 2020 19:24:41 +0900 Subject: [PATCH 11/37] =?UTF-8?q?5.01=5F=CE=B245?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VmdSizing_5.01_β45 miumiu ・数学ライブラリ  ・関数を概ねcython、pythonハイブリッド関数化  ・型宣言は未設定 --- src/executor.py | 2 +- src/module/MMath.pxd | 172 +++++++++++- src/module/MMath.pyx | 624 +++++++++++++++++++++++-------------------- vmdising_np64.spec | 2 +- 4 files changed, 499 insertions(+), 301 deletions(-) diff --git a/src/executor.py b/src/executor.py index 6f25d42..e86775d 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00_β44" +VERSION_NAME = "ver5.00_β45" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/module/MMath.pxd b/src/module/MMath.pxd index 460d8a5..462e2d7 100644 --- a/src/module/MMath.pxd +++ b/src/module/MMath.pxd @@ -23,35 +23,117 @@ cdef class MRect: cdef DTYPE_FLOAT_t __width cdef DTYPE_FLOAT_t __height + cpdef DTYPE_FLOAT_t x(self) + + cpdef DTYPE_FLOAT_t y(self) + + cpdef DTYPE_FLOAT_t width(self) + + cpdef DTYPE_FLOAT_t height(self) + cdef class MVector2D: cdef np.ndarray __data - cpdef data(self) + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] data(self) + + cpdef DTYPE_FLOAT_t x(self) + + cpdef DTYPE_FLOAT_t y(self) cpdef setX(self, x) cpdef setY(self, y) + cpdef double length(self) + + cpdef double lengthSquared(self) + + cpdef MVector2D normalized(self) + + cpdef normalize(self) + cpdef effective(self) + cdef class MVector3D: cdef np.ndarray __data - cpdef data(self) + cpdef MVector3D copy(self) + + cpdef double length(self) + + cpdef double lengthSquared(self) + + cpdef MVector3D normalized(self) + + cpdef normalize(self) + + cpdef double distanceToPoint(self, v) + + cpdef MVector3D project(self, modelView, projection, viewport) + + cpdef MVector3D unproject(self, modelView, projection, viewport) + + cpdef MVector4D toVector4D(self) + + cpdef bint is_almost_null(self) + cpdef MVector3D effective(self) + + cpdef MVector3D abs(self) + + cpdef MVector3D one(self) + + cpdef MVector3D non_zero(self) + + cpdef bint isnan(self) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] data(self) + + cpdef DTYPE_FLOAT_t x(self) + + cpdef DTYPE_FLOAT_t y(self) + + cpdef DTYPE_FLOAT_t z(self) + cpdef setX(self, x) cpdef setY(self, y) cpdef setZ(self, z) - cpdef effective(self) +cdef MVector3D crossProduct_MVector3D(v1, v2) + +cdef double dotProduct_MVector3D(v1, v2) + cdef class MVector4D: cdef np.ndarray __data - cpdef data(self) + cpdef double length(self) + cpdef double lengthSquared(self) + + cpdef MVector4D normalized(self) + + cpdef normalize(self) + + cpdef MVector3D toVector3D(self) + + cpdef bint is_almost_null(self) + + cpdef effective(self) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] data(self) + + cpdef DTYPE_FLOAT_t x(self) + + cpdef DTYPE_FLOAT_t y(self) + + cpdef DTYPE_FLOAT_t z(self) + + cpdef DTYPE_FLOAT_t w(self) + cpdef setX(self, x) cpdef setY(self, y) @@ -60,13 +142,49 @@ cdef class MVector4D: cpdef setW(self, w) - cpdef effective(self) +cdef double dotProduct_MVector4D(v1, v2) cdef class MQuaternion: cdef np.ndarray __data + cpdef MQuaternion copy(self) + + cpdef MQuaternion inverted(self) + + cpdef double length(self) + + cpdef double lengthSquared(self) + + cpdef MQuaternion normalized(self) + + cpdef normalize(self) + + cpdef effective(self) + + cpdef MMatrix4x4 toMatrix4x4(self) + + cpdef MVector4D toVector4D(self) + + cpdef MVector3D toEulerAngles4MMD(self) + + cpdef MVector3D toEulerAngles(self) + + cpdef double toDegree(self) + + cpdef double calcTheata(self, v) + cpdef data(self) + cpdef double x(self) + + cpdef double y(self) + + cpdef double z(self) + + cpdef double scalar(self) + + cpdef MVector3D vector(self) + cpdef setX(self, x) cpdef setY(self, y) @@ -75,10 +193,35 @@ cdef class MQuaternion: cpdef setScalar(self, w) +cdef dotProduct_MQuaternion(v1, v2) + +cdef MQuaternion fromAxisAndAngle(vec3, angle) + +cdef MQuaternion fromAxisAndQuaternion(vec3, qq) + +cdef MQuaternion fromDirection(direction, up) + +cdef MQuaternion fromAxes(xAxis, yAxis, zAxis) + +cdef MQuaternion fromRotationMatrix(rot3x3) + +cdef MQuaternion rotationTo(fromv, tov) + +cdef MQuaternion fromEulerAngles(pitch, yaw, roll) + +cdef MQuaternion nlerp(q1, q2, t) + +cdef MQuaternion slerp(q1, q2, t) + + cdef class MMatrix4x4: cdef np.ndarray __data - cpdef data(self) + cpdef MMatrix4x4 copy(self) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=2] data(self) + + cpdef MMatrix4x4 inverted(self) cpdef rotate(self, qq) @@ -86,5 +229,20 @@ cdef class MMatrix4x4: cpdef scale(self, vec3) + cpdef setToIdentity(self) + cpdef lookAt(self, eye, center, up) - \ No newline at end of file + + cpdef perspective(self, verticalAngle, aspectRatio, nearPlane, farPlane) + + cpdef MVector3D mapVector(self, vector) + + cpdef MQuaternion toQuaternion(self) + + +cpdef bint is_almost_null(v) + +cpdef double get_effective_value(v) + +cpdef double get_almost_zero_value(v) + diff --git a/src/module/MMath.pyx b/src/module/MMath.pyx index a2ac159..b827b4a 100644 --- a/src/module/MMath.pyx +++ b/src/module/MMath.pyx @@ -21,17 +21,17 @@ cdef class MRect: self.__width = width self.__height = height - def x(self): - return int(self.__x) + cpdef DTYPE_FLOAT_t x(self): + return self.__x - def y(self): - return int(self.__y) + cpdef DTYPE_FLOAT_t y(self): + return self.__y - def width(self): - return int(self.__width) + cpdef DTYPE_FLOAT_t width(self): + return self.__width - def height(self): - return int(self.__height) + cpdef DTYPE_FLOAT_t height(self): + return self.__height def __str__(self): return "MRect({0}, {1}, {2}, {3})".format(self.__x, self.__y, self.__width, self.__height) @@ -52,22 +52,22 @@ cdef class MVector2D: else: self.__data = np.array([x, y], dtype=np.float64) - def length(self): + cpdef double length(self): return float(np.linalg.norm(self.data(), ord=2)) - def lengthSquared(self): + cpdef double lengthSquared(self): return float(np.linalg.norm(self.data(), ord=2)**2) - def normalized(self): - l2 = np.linalg.norm(self.data(), ord=2, axis=-1, keepdims=True) + cpdef MVector2D normalized(self): + cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] l2 = np.linalg.norm(self.data(), ord=2, axis=-1, keepdims=True) l2[l2 == 0] = 1 - normv = self.data() / l2 + cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] normv = self.data() / l2 return MVector2D(normv[0], normv[1]) - def normalize(self): - l2 = np.linalg.norm(self.data(), ord=2, axis=-1, keepdims=True) + cpdef normalize(self): + cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] l2 = np.linalg.norm(self.data(), ord=2, axis=-1, keepdims=True) l2[l2 == 0] = 1 - normv = self.data() / l2 + cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] normv = self.data() / l2 self.__data = normv cpdef effective(self): @@ -76,11 +76,11 @@ cdef class MVector2D: return self - cpdef data(self): + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] data(self): return self.__data def __str__(self): - return "MVector2D({0}, {1})".format(self.data()[0], self.data()[1]) + return "MVector2D({0}, {1})".format(self.__data[0], self.__data[1]) def __lt__(self, other): return np.all(self.data() < other.data()) @@ -191,19 +191,19 @@ cdef class MVector2D: return v2 def __neg__(self): - return self.__class__(-self.data()[0], -self.data()[1]) + return self.__class__(-self.__data[0], -self.__data[1]) def __pos__(self): - return self.__class__(+self.data()[0], +self.data()[1]) + return self.__class__(+self.__data[0], +self.__data[1]) def __invert__(self): - return self.__class__(~self.data()[0], ~self.data()[1]) + return self.__class__(~self.__data[0], ~self.__data[1]) - def x(self): - return self.data()[0] + cpdef DTYPE_FLOAT_t x(self): + return self.__data[0] - def y(self): - return self.data()[1] + cpdef DTYPE_FLOAT_t y(self): + return self.__data[1] cpdef setX(self, x): self.__data[0] = x @@ -227,32 +227,32 @@ cdef class MVector3D: else: self.__data = np.array([x, y, z], dtype=np.float64) - def copy(self): + cpdef MVector3D copy(self): return MVector3D(self.x(), self.y(), self.z()) - def length(self): - return np.linalg.norm(self.data(), ord=2) + cpdef double length(self): + return float(np.linalg.norm(self.data(), ord=2)) - def lengthSquared(self): - return np.linalg.norm(self.data(), ord=2)**2 + cpdef double lengthSquared(self): + return float(np.linalg.norm(self.data(), ord=2)**2) - def normalized(self): + cpdef MVector3D normalized(self): l2 = np.linalg.norm(self.data(), ord=2, axis=-1, keepdims=True) l2[l2 == 0] = 1 normv = self.data() / l2 return MVector3D(normv[0], normv[1], normv[2]) - def normalize(self): + cpdef normalize(self): self.effective() l2 = np.linalg.norm(self.data(), ord=2, axis=-1, keepdims=True) l2[l2 == 0] = 1 normv = self.data() / l2 self.__data = normv - def distanceToPoint(self, v): + cpdef double distanceToPoint(self, v): return MVector3D(self.data() - v.data()).length() - def project(self, modelView, projection, viewport): + cpdef MVector3D project(self, modelView, projection, viewport): tmp = MVector4D(self.x(), self.y(), self.z(), 1) tmp = projection * modelView * tmp if is_almost_null(tmp.w()): @@ -267,7 +267,7 @@ cdef class MVector3D: return tmp.toVector3D() - def unproject(self, modelView, projection, viewport): + cpdef MVector3D unproject(self, modelView, projection, viewport): inverse = (projection * modelView).inverted() tmp = MVector4D(self.x(), self.y(), self.z(), 1) @@ -285,26 +285,26 @@ cdef class MVector3D: return obj.toVector3D() - def toVector4D(self): - return MVector4D(self.data()[0], self.data()[1], self.data()[2], 0) + cpdef MVector4D toVector4D(self): + return MVector4D(self.__data[0], self.__data[1], self.__data[2], 0) - def is_almost_null(self): - return (is_almost_null(self.data()[0]) and is_almost_null(self.data()[1]) and is_almost_null(self.data()[2])) + cpdef bint is_almost_null(self): + return (is_almost_null(self.__data[0]) and is_almost_null(self.__data[1]) and is_almost_null(self.__data[2])) - cpdef effective(self): + cpdef MVector3D effective(self): self.__data[np.isnan(self.data())] = 0 self.__data[np.isinf(self.data())] = 0 return self - def abs(self): + cpdef MVector3D abs(self): self.setX(abs(get_effective_value(self.x()))) self.setY(abs(get_effective_value(self.y()))) self.setZ(abs(get_effective_value(self.z()))) return self - def one(self): + cpdef MVector3D one(self): self.effective() self.setX(1 if is_almost_null(self.x()) else self.x()) self.setY(1 if is_almost_null(self.y()) else self.y()) @@ -312,7 +312,7 @@ cdef class MVector3D: return self - def non_zero(self): + cpdef MVector3D non_zero(self): self.effective() self.setX(0.0001 if is_almost_null(self.x()) else self.x()) self.setY(0.0001 if is_almost_null(self.y()) else self.y()) @@ -320,28 +320,26 @@ cdef class MVector3D: return self - def isnan(self): + cpdef bint isnan(self): self.__data = self.data().astype(np.float64) return np.isnan(self.data()).any() - + @classmethod def crossProduct(cls, v1, v2): - crossv = np.cross(v1.data(), v2.data()) - return MVector3D(crossv[0], crossv[1], crossv[2]) + return crossProduct_MVector3D(v1, v2) @classmethod def dotProduct(cls, v1, v2): - dotv = np.dot(v1.data(), v2.data()) - return dotv + return dotProduct_MVector3D(v1, v2) - cpdef data(self): + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] data(self): return self.__data def to_log(self): - return "x: {0}, y: {1} z: {2}".format(round(self.data()[0], 5), round(self.data()[1], 5), round(self.data()[2], 5)) + return "x: {0}, y: {1} z: {2}".format(round(self.__data[0], 5), round(self.__data[1], 5), round(self.__data[2], 5)) def __str__(self): - return "MVector3D({0}, {1}, {2})".format(self.data()[0], self.data()[1], self.data()[2]) + return "MVector3D({0}, {1}, {2})".format(self.__data[0], self.__data[1], self.__data[2]) def __lt__(self, other): return np.all(self.data() < other.data()) @@ -452,22 +450,22 @@ cdef class MVector3D: return v2 def __neg__(self): - return self.__class__(-self.data()[0], -self.data()[1], -self.data()[2]) + return self.__class__(-self.__data[0], -self.__data[1], -self.__data[2]) def __pos__(self): - return self.__class__(+self.data()[0], +self.data()[1], +self.data()[2]) + return self.__class__(+self.__data[0], +self.__data[1], +self.__data[2]) def __invert__(self): - return self.__class__(~self.data()[0], ~self.data()[1], ~self.data()[2]) + return self.__class__(~self.__data[0], ~self.__data[1], ~self.__data[2]) - def x(self): - return self.data()[0] + cpdef DTYPE_FLOAT_t x(self): + return self.__data[0] - def y(self): - return self.data()[1] + cpdef DTYPE_FLOAT_t y(self): + return self.__data[1] - def z(self): - return self.data()[2] + cpdef DTYPE_FLOAT_t z(self): + return self.__data[2] cpdef setX(self, x): self.__data[0] = x @@ -479,6 +477,16 @@ cdef class MVector3D: self.__data[2] = z +cdef MVector3D crossProduct_MVector3D(v1, v2): + crossv = np.cross(v1.data(), v2.data()) + return MVector3D(crossv[0], crossv[1], crossv[2]) + + +cdef double dotProduct_MVector3D(v1, v2): + dotv = np.dot(v1.data(), v2.data()) + return dotv + + cdef class MVector4D: def __init__(self, x=0.0, y=0.0, z=0.0, w=0.0): @@ -493,29 +501,29 @@ cdef class MVector4D: else: self.__data = np.array([x, y, z, w], dtype=np.float64) - def length(self): + cpdef double length(self): return np.linalg.norm(self.data(), ord=2) - def lengthSquared(self): + cpdef double lengthSquared(self): return np.linalg.norm(self.data(), ord=2)**2 - def normalized(self): + cpdef MVector4D normalized(self): l2 = np.linalg.norm(self.data(), ord=2, axis=-1, keepdims=True) l2[l2 == 0] = 1 normv = self.data() / l2 return MVector4D(normv[0], normv[1], normv[2], normv[3]) - def normalize(self): + cpdef normalize(self): l2 = np.linalg.norm(self.data(), ord=2, axis=-1, keepdims=True) l2[l2 == 0] = 1 normv = self.data() / l2 self.__data = normv - def toVector3D(self): - return MVector3D(self.data()[0], self.data()[1], self.data()[2]) + cpdef MVector3D toVector3D(self): + return MVector3D(self.__data[0], self.__data[1], self.__data[2]) - def is_almost_null(self): - return (is_almost_null(self.data()[0]) and is_almost_null(self.data()[1]) and is_almost_null(self.data()[2]) and is_almost_null(self.data()[3])) + cpdef bint is_almost_null(self): + return (is_almost_null(self.__data[0]) and is_almost_null(self.__data[1]) and is_almost_null(self.__data[2]) and is_almost_null(self.__data[3])) cpdef effective(self): self.__data[np.isnan(self.data())] = 0 @@ -523,14 +531,13 @@ cdef class MVector4D: @classmethod def dotProduct(cls, v1, v2): - dotv = np.dot(v1.data(), v2.data()) - return dotv + return dotProduct_MVector4D(v1, v2) - cpdef data(self): + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] data(self): return self.__data def __str__(self): - return "MVector4D({0}, {1}, {2}, {3})".format(self.data()[0], self.data()[1], self.data()[2], self.data()[3]) + return "MVector4D({0}, {1}, {2}, {3})".format(self.__data[0], self.__data[1], self.__data[2], self.__data[3]) def __lt__(self, other): return np.all(self.data() < other.data()) @@ -641,25 +648,25 @@ cdef class MVector4D: return v2 def __neg__(self): - return self.__class__(-self.data()[0], -self.data()[1], -self.data()[2], -self.data()[3]) + return self.__class__(-self.__data[0], -self.__data[1], -self.__data[2], -self.__data[3]) def __pos__(self): - return self.__class__(+self.data()[0], +self.data()[1], +self.data()[2], +self.data()[3]) + return self.__class__(+self.__data[0], +self.__data[1], +self.__data[2], +self.__data[3]) def __invert__(self): - return self.__class__(~self.data()[0], ~self.data()[1], ~self.data()[2], ~self.data()[3]) + return self.__class__(~self.__data[0], ~self.__data[1], ~self.__data[2], ~self.__data[3]) - def x(self): - return self.data()[0] + cpdef DTYPE_FLOAT_t x(self): + return self.__data[0] - def y(self): - return self.data()[1] + cpdef DTYPE_FLOAT_t y(self): + return self.__data[1] - def z(self): - return self.data()[2] + cpdef DTYPE_FLOAT_t z(self): + return self.__data[2] - def w(self): - return self.data()[3] + cpdef DTYPE_FLOAT_t w(self): + return self.__data[3] cpdef setX(self, x): self.__data[0] = x @@ -674,6 +681,11 @@ cdef class MVector4D: self.__data[3] = w +cdef double dotProduct_MVector4D(v1, v2): + dotv = np.dot(v1.data(), v2.data()) + return dotv + + cdef class MQuaternion: def __init__(self, w=1.0, x=0.0, y=0.0, z=0.0): @@ -691,37 +703,37 @@ cdef class MQuaternion: else: self.__data = np.array([w, x, y, z], dtype=np.float64) - def copy(self): + cpdef MQuaternion copy(self): return MQuaternion(self.scalar(), self.x(), self.y(), self.z()) def __str__(self): return "MQuaternion({0}, {1}, {2}, {3})".format(self.scalar(), self.x(), self.y(), self.z()) - def inverted(self): + cpdef MQuaternion inverted(self): v = self.data().inverse() return self.__class__(v.w, v.x, v.y, v.z) - def length(self): + cpdef double length(self): return self.data().abs() - def lengthSquared(self): + cpdef double lengthSquared(self): return self.data().abs()**2 - def normalized(self): + cpdef MQuaternion normalized(self): self.effective() v = self.data().normalized() return MQuaternion(v.w, v.x, v.y, v.z) - def normalize(self): + cpdef normalize(self): self.__data = self.data().normalized().components - def effective(self): + cpdef effective(self): self.data().components[np.isnan(self.data().components)] = 0 self.data().components[np.isinf(self.data().components)] = 0 # Scalarは1がデフォルトとなる self.setScalar(1 if self.scalar() == 0 else self.scalar()) - def toMatrix4x4(self): + cpdef MMatrix4x4 toMatrix4x4(self): mat = MMatrix4x4() m = mat.data() @@ -753,17 +765,17 @@ cdef class MQuaternion: return mat - def toVector4D(self): + cpdef MVector4D toVector4D(self): return MVector4D(self.data().x, self.data().y, self.data().z, self.data().w) - def toEulerAngles4MMD(self): + cpdef MVector3D toEulerAngles4MMD(self): # MMDの表記に合わせたオイラー角 euler = self.toEulerAngles() return MVector3D(euler.x(), -euler.y(), -euler.z()) # http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q37 - def toEulerAngles(self): + cpdef MVector3D toEulerAngles(self): xp = self.data().x yp = self.data().y zp = self.data().z @@ -811,217 +823,68 @@ cdef class MQuaternion: return v # 角度に変換 - def toDegree(self): + cpdef double toDegree(self): return math.degrees(2 * math.acos(min(1, max(-1, self.scalar())))) # 自分ともうひとつの値vとのtheta(変位量)を返す - def calcTheata(self, v): + cpdef double calcTheata(self, v): dot = MQuaternion.dotProduct(self.normalized(), v.normalized()) theta = math.acos(min(1, max(-1, dot))) return theta @classmethod def dotProduct(cls, v1, v2): - dotv = np.sum(v1.data().components * v2.data().components) - return dotv + return dotProduct_MQuaternion(v1, v2) @classmethod def fromAxisAndAngle(cls, vec3: MVector3D, angle: float): - x = vec3.x() - y = vec3.y() - z = vec3.z() - length = math.sqrt(x * x + y * y + z * z) - - if not is_almost_null(length - 1.0) and not is_almost_null(length): - x /= length - y /= length - z /= length - - a = math.radians(angle / 2.0) - s = math.sin(a) - c = math.cos(a) - return MQuaternion(c, x * s, y * s, z * s).normalized() + return fromAxisAndAngle(vec3, angle) @classmethod def fromAxisAndQuaternion(cls, vec3: MVector3D, qq): - qq.normalize() - - x = vec3.x() - y = vec3.y() - z = vec3.z() - length = math.sqrt(x * x + y * y + z * z) - - if not is_almost_null(length - 1.0) and not is_almost_null(length): - x /= length - y /= length - z /= length - - a = math.acos(min(1, max(-1, qq.scalar()))) - s = math.sin(a) - c = math.cos(a) + return fromAxisAndQuaternion(vec3, qq) - # logger.test("scalar: %s, a: %s, c: %s, degree: %s", qq.scalar(), a, c, math.degrees(2 * math.acos(min(1, max(-1, qq.scalar()))))) - - return MQuaternion(c, x * s, y * s, z * s).normalized() - @classmethod def fromDirection(cls, direction, up): - if direction.is_almost_null: - return MQuaternion() - - zAxis = direction.normalized() - xAxis = MVector3D.crossProduct(up, zAxis) - if (is_almost_null(xAxis.lengthSquared())): - # collinear or invalid up vector derive shortest arc to new direction - return MQuaternion.rotationTo(MVector3D(0.0, 0.0, 1.0), zAxis) - - xAxis.normalize() - yAxis = MVector3D.crossProduct(zAxis, xAxis) - return MQuaternion.fromAxes(xAxis, yAxis, zAxis) + return fromDirection(direction, up) @classmethod def fromAxes(cls, xAxis, yAxis, zAxis): - rot3x3 = np.array([[xAxis.x(), yAxis.x(), zAxis.x()], [xAxis.y(), yAxis.y(), zAxis.y()], [xAxis.z(), yAxis.z(), zAxis.z()]]) - return MQuaternion.fromRotationMatrix(rot3x3) - + return fromAxes(xAxis, yAxis, zAxis) + @classmethod def fromRotationMatrix(cls, rot3x3): - scalar = 0 - axis = np.zeros(3) - - trace = rot3x3[0][0] + rot3x3[1][1] + rot3x3[2][2] - if trace > 0.00000001: - s = 2.0 * math.sqrt(trace + 1.0) - scalar = 0.25 * s - axis[0] = (rot3x3[2][1] - rot3x3[1][2]) / s - axis[1] = (rot3x3[0][2] - rot3x3[2][0]) / s - axis[2] = (rot3x3[1][0] - rot3x3[0][1]) / s - else: - s_next = np.array([1, 2, 0], dtype=np.int8) - i = 0 - if rot3x3[1][1] > rot3x3[0][0]: - i = 1 - if rot3x3[2][2] > rot3x3[i][i]: - i = 2 - - j = s_next[i] - k = s_next[j] - - s = 2.0 * math.sqrt(rot3x3[i][i] - rot3x3[j][j] - rot3x3[k][k] + 1.0) - axis[i] = 0.25 * s - - scalar = (rot3x3[k][j] - rot3x3[j][k]) / s - axis[j] = (rot3x3[j][i] + rot3x3[i][j]) / s - axis[k] = (rot3x3[k][i] + rot3x3[i][k]) / s - - return MQuaternion(scalar, axis[0], axis[1], axis[2]) + return fromRotationMatrix(rot3x3) @classmethod def rotationTo(cls, fromv, tov): - v0 = fromv.normalized() - v1 = tov.normalized() - d = MVector3D.dotProduct(v0, v1) + 1.0 - - # if dest vector is close to the inverse of source vector, ANY axis of rotation is valid - if is_almost_null(d): - axis = MVector3D.crossProduct(MVector3D(1.0, 0.0, 0.0), v0) - if is_almost_null(axis.lengthSquared()): - axis = MVector3D.crossProduct(MVector3D(0.0, 1.0, 0.0), v0) - axis.normalize() - # same as MQuaternion.fromAxisAndAngle(axis, 180.0) - return MQuaternion(0.0, axis.x(), axis.y(), axis.z()).normalized() - - d = math.sqrt(2.0 * d) - axis = MVector3D.crossProduct(v0, v1) / d - return MQuaternion(d * 0.5, axis.x(), axis.y(), axis.z()).normalized() - + return rotationTo(fromv, tov) + @classmethod def fromEulerAngles(cls, pitch, yaw, roll): - pitch = math.radians(pitch) - yaw = math.radians(yaw) - roll = math.radians(roll) - - pitch *= 0.5 - yaw *= 0.5 - roll *= 0.5 - - c1 = math.cos(yaw) - s1 = math.sin(yaw) - c2 = math.cos(roll) - s2 = math.sin(roll) - c3 = math.cos(pitch) - s3 = math.sin(pitch) - c1c2 = c1 * c2 - s1s2 = s1 * s2 - w = c1c2 * c3 + s1s2 * s3 - x = c1c2 * s3 + s1s2 * c3 - y = s1 * c2 * c3 - c1 * s2 * s3 - z = c1 * s2 * c3 - s1 * c2 * s3 - - return MQuaternion(w, x, y, z) - + return fromEulerAngles(pitch, yaw, roll) + @classmethod def nlerp(cls, q1, q2, t): - # Handle the easy cases first. - if t <= 0.0: - return q1 - elif t >= 1.0: - return q2 - - # Determine the angle between the two quaternions. - q2b = MQuaternion(q2.scalar(), q2.x(), q2.y(), q2.z()) - - dot = MQuaternion.dotProduct(q1, q2) - if dot < 0.0: - q2b = -q2b - - # Perform the linear interpolation. - return (q1 * (1.0 - t) + q2b * t).normalized() + return nlerp(q1, q2, t) @classmethod def slerp(cls, q1, q2, t): - # Handle the easy cases first. - if t <= 0.0: - return q1 - elif t >= 1.0: - return q2 - - # Determine the angle between the two quaternions. - q2b = MQuaternion(q2.scalar(), q2.x(), q2.y(), q2.z()) - dot = MQuaternion.dotProduct(q1, q2) - - if dot < 0.0: - q2b = -q2b - dot = -dot - - # Get the scale factors. If they are too small, - # then revert to simple linear interpolation. - factor1 = 1.0 - t - factor2 = t - - if (1.0 - dot) > 0.0000001: - angle = math.acos(max(0, min(1, dot))) - sinOfAngle = math.sin(angle) - if sinOfAngle > 0.0000001: - factor1 = math.sin((1.0 - t) * angle) / sinOfAngle - factor2 = math.sin(t * angle) / sinOfAngle - - # Construct the result quaternion. - return q1 * factor1 + q2b * factor2 - - def x(self): + return slerp(q1, q2, t) + + cpdef double x(self): return self.data().x - def y(self): + cpdef double y(self): return self.data().y - def z(self): + cpdef double z(self): return self.data().z - def scalar(self): + cpdef double scalar(self): return self.data().w - def vector(self): + cpdef MVector3D vector(self): return MVector3D(self.data().x, self.data().y, self.data().z) cpdef setX(self, x): @@ -1139,6 +1002,186 @@ cdef class MQuaternion: return self.__class__(~self.data().w, ~self.data().x, ~self.data().y, ~self.data().z) +cdef dotProduct_MQuaternion(v1, v2): + dotv = np.sum(v1.data().components * v2.data().components) + return dotv + +cdef MQuaternion fromAxisAndAngle(vec3, angle): + x = vec3.x() + y = vec3.y() + z = vec3.z() + length = math.sqrt(x * x + y * y + z * z) + + if not is_almost_null(length - 1.0) and not is_almost_null(length): + x /= length + y /= length + z /= length + + a = math.radians(angle / 2.0) + s = math.sin(a) + c = math.cos(a) + return MQuaternion(c, x * s, y * s, z * s).normalized() + +cdef MQuaternion fromAxisAndQuaternion(vec3, qq): + qq.normalize() + + x = vec3.x() + y = vec3.y() + z = vec3.z() + length = math.sqrt(x * x + y * y + z * z) + + if not is_almost_null(length - 1.0) and not is_almost_null(length): + x /= length + y /= length + z /= length + + a = math.acos(min(1, max(-1, qq.scalar()))) + s = math.sin(a) + c = math.cos(a) + + # logger.test("scalar: %s, a: %s, c: %s, degree: %s", qq.scalar(), a, c, math.degrees(2 * math.acos(min(1, max(-1, qq.scalar()))))) + + return MQuaternion(c, x * s, y * s, z * s).normalized() + +cdef MQuaternion fromDirection(direction, up): + if direction.is_almost_null: + return MQuaternion() + + zAxis = direction.normalized() + xAxis = MVector3D.crossProduct(up, zAxis) + if (is_almost_null(xAxis.lengthSquared())): + # collinear or invalid up vector derive shortest arc to new direction + return MQuaternion.rotationTo(MVector3D(0.0, 0.0, 1.0), zAxis) + + xAxis.normalize() + yAxis = MVector3D.crossProduct(zAxis, xAxis) + return MQuaternion.fromAxes(xAxis, yAxis, zAxis) + +cdef MQuaternion fromAxes(xAxis, yAxis, zAxis): + rot3x3 = np.array([[xAxis.x(), yAxis.x(), zAxis.x()], [xAxis.y(), yAxis.y(), zAxis.y()], [xAxis.z(), yAxis.z(), zAxis.z()]]) + return MQuaternion.fromRotationMatrix(rot3x3) + +cdef MQuaternion fromRotationMatrix(rot3x3): + scalar = 0 + axis = np.zeros(3) + + trace = rot3x3[0][0] + rot3x3[1][1] + rot3x3[2][2] + if trace > 0.00000001: + s = 2.0 * math.sqrt(trace + 1.0) + scalar = 0.25 * s + axis[0] = (rot3x3[2][1] - rot3x3[1][2]) / s + axis[1] = (rot3x3[0][2] - rot3x3[2][0]) / s + axis[2] = (rot3x3[1][0] - rot3x3[0][1]) / s + else: + s_next = np.array([1, 2, 0], dtype=np.int8) + i = 0 + if rot3x3[1][1] > rot3x3[0][0]: + i = 1 + if rot3x3[2][2] > rot3x3[i][i]: + i = 2 + + j = s_next[i] + k = s_next[j] + + s = 2.0 * math.sqrt(rot3x3[i][i] - rot3x3[j][j] - rot3x3[k][k] + 1.0) + axis[i] = 0.25 * s + + scalar = (rot3x3[k][j] - rot3x3[j][k]) / s + axis[j] = (rot3x3[j][i] + rot3x3[i][j]) / s + axis[k] = (rot3x3[k][i] + rot3x3[i][k]) / s + + return MQuaternion(scalar, axis[0], axis[1], axis[2]) + +cdef MQuaternion rotationTo(fromv, tov): + v0 = fromv.normalized() + v1 = tov.normalized() + d = MVector3D.dotProduct(v0, v1) + 1.0 + + # if dest vector is close to the inverse of source vector, ANY axis of rotation is valid + if is_almost_null(d): + axis = MVector3D.crossProduct(MVector3D(1.0, 0.0, 0.0), v0) + if is_almost_null(axis.lengthSquared()): + axis = MVector3D.crossProduct(MVector3D(0.0, 1.0, 0.0), v0) + axis.normalize() + # same as MQuaternion.fromAxisAndAngle(axis, 180.0) + return MQuaternion(0.0, axis.x(), axis.y(), axis.z()).normalized() + + d = math.sqrt(2.0 * d) + axis = MVector3D.crossProduct(v0, v1) / d + return MQuaternion(d * 0.5, axis.x(), axis.y(), axis.z()).normalized() + +cdef MQuaternion fromEulerAngles(pitch, yaw, roll): + pitch = math.radians(pitch) + yaw = math.radians(yaw) + roll = math.radians(roll) + + pitch *= 0.5 + yaw *= 0.5 + roll *= 0.5 + + c1 = math.cos(yaw) + s1 = math.sin(yaw) + c2 = math.cos(roll) + s2 = math.sin(roll) + c3 = math.cos(pitch) + s3 = math.sin(pitch) + c1c2 = c1 * c2 + s1s2 = s1 * s2 + w = c1c2 * c3 + s1s2 * s3 + x = c1c2 * s3 + s1s2 * c3 + y = s1 * c2 * c3 - c1 * s2 * s3 + z = c1 * s2 * c3 - s1 * c2 * s3 + + return MQuaternion(w, x, y, z) + +cdef MQuaternion nlerp(q1, q2, t): + # Handle the easy cases first. + if t <= 0.0: + return q1 + elif t >= 1.0: + return q2 + + # Determine the angle between the two quaternions. + q2b = MQuaternion(q2.scalar(), q2.x(), q2.y(), q2.z()) + + dot = MQuaternion.dotProduct(q1, q2) + if dot < 0.0: + q2b = -q2b + + # Perform the linear interpolation. + return (q1 * (1.0 - t) + q2b * t).normalized() + +cdef MQuaternion slerp(q1, q2, t): + # Handle the easy cases first. + if t <= 0.0: + return q1 + elif t >= 1.0: + return q2 + + # Determine the angle between the two quaternions. + q2b = MQuaternion(q2.scalar(), q2.x(), q2.y(), q2.z()) + dot = MQuaternion.dotProduct(q1, q2) + + if dot < 0.0: + q2b = -q2b + dot = -dot + + # Get the scale factors. If they are too small, + # then revert to simple linear interpolation. + factor1 = 1.0 - t + factor2 = t + + if (1.0 - dot) > 0.0000001: + angle = math.acos(max(0, min(1, dot))) + sinOfAngle = math.sin(angle) + if sinOfAngle > 0.0000001: + factor1 = math.sin((1.0 - t) * angle) / sinOfAngle + factor2 = math.sin(t * angle) / sinOfAngle + + # Construct the result quaternion. + return q1 * factor1 + q2b * factor2 + + cdef class MMatrix4x4: def __init__(self, m11=1.0, m12=0.0, m13=0.0, m14=0.0, m21=0.0, m22=1.0, m23=0.0, m24=0.0, m31=0.0, m32=0.0, m33=1.0, m34=0.0, m41=0.0, m42=0.0, m43=0.0, m44=1.0): @@ -1146,10 +1189,10 @@ cdef class MMatrix4x4: self.__data = np.array([[m11, m12, m13, m14], [m21, m22, m23, m24], [m31, m32, m33, m34], [m41, m42, m43, m44]], dtype=np.float64) elif isinstance(m11, MMatrix4x4): # 行列クラスの場合 - self.__data = np.array([[m11.data()[0, 0], m11.data()[0, 1], m11.data()[0, 2], m11.data()[0, 3]], \ - [m11.data()[1, 0], m11.data()[1, 1], m11.data()[1, 2], m11.data()[1, 3]], \ - [m11.data()[2, 0], m11.data()[2, 1], m11.data()[2, 2], m11.data()[2, 3]], \ - [m11.data()[3, 0], m11.data()[3, 1], m11.data()[3, 2], m11.data()[3, 3]]], dtype=np.float64) + self.__data = np.array([[m11.__data[0, 0], m11.__data[0, 1], m11.__data[0, 2], m11.__data[0, 3]], \ + [m11.__data[1, 0], m11.__data[1, 1], m11.__data[1, 2], m11.__data[1, 3]], \ + [m11.__data[2, 0], m11.__data[2, 1], m11.__data[2, 2], m11.__data[2, 3]], \ + [m11.__data[3, 0], m11.__data[3, 1], m11.__data[3, 2], m11.__data[3, 3]]], dtype=np.float64) elif isinstance(m11, np.ndarray): # 行列そのものの場合 self.__data = np.array([[m11[0, 0], m11[0, 1], m11[0, 2], m11[0, 3]], [m11[1, 0], m11[1, 1], m11[1, 2], m11[1, 3]], \ @@ -1158,14 +1201,14 @@ cdef class MMatrix4x4: # べた値の場合 self.__data = np.array([[m11, m12, m13, m14], [m21, m22, m23, m24], [m31, m32, m33, m34], [m41, m42, m43, m44]], dtype=np.float64) - def copy(self): + cpdef MMatrix4x4 copy(self): return MMatrix4x4(self.data()) - cpdef data(self): + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=2] data(self): return self.__data # 逆行列 - def inverted(self): + cpdef MMatrix4x4 inverted(self): v = np.linalg.inv(self.data()) return MMatrix4x4(v) @@ -1186,7 +1229,7 @@ cdef class MMatrix4x4: self.__data[:, :3] *= vec_mat # 単位行列 - def setToIdentity(self): + cpdef setToIdentity(self): self.__data = np.eye(4) cpdef lookAt(self, eye, center, up): @@ -1208,7 +1251,7 @@ cdef class MMatrix4x4: self *= m self.translate(-eye) - def perspective(self, verticalAngle, aspectRatio, nearPlane, farPlane): + cpdef perspective(self, verticalAngle, aspectRatio, nearPlane, farPlane): if nearPlane == farPlane or aspectRatio == 0: return @@ -1222,28 +1265,25 @@ cdef class MMatrix4x4: clip = farPlane - nearPlane m = MMatrix4x4() - md = m.data() - md[0, 0] = cotan / aspectRatio - md[1, 1] = cotan - md[2, 2] = -(nearPlane + farPlane) / clip - md[2, 3] = -(2 * nearPlane * farPlane) / clip - md[3, 2] = -1 + m.__data[0, 0] = cotan / aspectRatio + m.__data[1, 1] = cotan + m.__data[2, 2] = -(nearPlane + farPlane) / clip + m.__data[2, 3] = -(2 * nearPlane * farPlane) / clip + m.__data[3, 2] = -1 self *= m - def mapVector(self, vector): + cpdef MVector3D mapVector(self, vector): vec_mat = np.array([vector.x(), vector.y(), vector.z()]) - d = self.data() - xyz = np.sum(vec_mat * d[:3, :3], axis=1) + xyz = np.sum(vec_mat * self.__data[:3, :3], axis=1) return MVector3D(xyz[0], xyz[1], xyz[2]) - def toQuaternion(self): - d = self.data() - a = [[d[0, 0], d[0, 1], d[0, 2], d[0, 3]], - [d[1, 0], d[1, 1], d[1, 2], d[1, 3]], - [d[2, 0], d[2, 1], d[2, 2], d[2, 3]], - [d[3, 0], d[3, 1], d[3, 2], d[3, 3]]] + cpdef MQuaternion toQuaternion(self): + a = [[self.__data[0, 0], self.__data[0, 1], self.__data[0, 2], self.__data[0, 3]], + [self.__data[1, 0], self.__data[1, 1], self.__data[1, 2], self.__data[1, 3]], + [self.__data[2, 0], self.__data[2, 1], self.__data[2, 2], self.__data[2, 3]], + [self.__data[3, 0], self.__data[3, 1], self.__data[3, 2], self.__data[3, 3]]] q = MQuaternion() @@ -1366,11 +1406,11 @@ cdef class MMatrix4x4: return self -def is_almost_null(v): +cpdef bint is_almost_null(v): return abs(v) < 0.0000001 -def get_effective_value(v): +cpdef double get_effective_value(v): if math.isnan(v): return 0 @@ -1380,7 +1420,7 @@ def get_effective_value(v): return v -def get_almost_zero_value(v): +cpdef double get_almost_zero_value(v): if get_effective_value(v) == 0: return 0 diff --git a/vmdising_np64.spec b/vmdising_np64.spec index 6c09a77..25e344e 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,7 +26,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β44_64bit', + name='VmdSizing_5.01_β45_64bit', debug=False, bootloader_ignore_signals=False, strip=False, From 0de8b5b73d78d6f5dfc86a3b8baa076d8db43578 Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Sat, 26 Sep 2020 20:43:52 +0900 Subject: [PATCH 12/37] =?UTF-8?q?5.01=5F=CE=B246?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VmdSizing_5.01_β46 miumiu ・数学ライブラリ  ・比較関数、四則演算cython対応 ・出力VMD上書き処理のチェックタイミングを修正  ・変換先モデル指定→出力VMDパス生成→変換先モデル変更→出力VMDパス変更の流れになってるはず --- src/executor.py | 2 +- src/form/MainFrame.py | 6 +- src/form/panel/FilePanel.py | 2 +- src/module/MMath.pxd | 131 +++++++++ src/module/MMath.pyx | 520 ++++++++++++++++++++++++++++-------- vmdising_np64.spec | 2 +- 6 files changed, 540 insertions(+), 123 deletions(-) diff --git a/src/executor.py b/src/executor.py index e86775d..1dd9597 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00_β45" +VERSION_NAME = "ver5.00_β46" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/form/MainFrame.py b/src/form/MainFrame.py index 7a310d5..20f167c 100644 --- a/src/form/MainFrame.py +++ b/src/form/MainFrame.py @@ -370,11 +370,13 @@ def on_load_result(self, event: wx.Event): # そのまま実行する場合、サイジング実行処理に遷移 # 念のため出力ファイルパス自動生成(空の場合設定) - self.file_panel_ctrl.file_set.set_output_vmd_path(event) + if not self.file_panel_ctrl.file_set.output_vmd_file_ctrl.file_ctrl.GetPath(): + self.file_panel_ctrl.file_set.set_output_vmd_path(event) # multiのも出力ファイルパス自動生成(空の場合設定) for file_set in self.multi_panel_ctrl.file_set_list: - file_set.set_output_vmd_path(event) + if not file_set.output_vmd_file_ctrl.file_ctrl.GetPath(): + file_set.set_output_vmd_path(event) # フォーム無効化 self.file_panel_ctrl.disable() diff --git a/src/form/panel/FilePanel.py b/src/form/panel/FilePanel.py index 75dfd57..3a94377 100644 --- a/src/form/panel/FilePanel.py +++ b/src/form/panel/FilePanel.py @@ -228,7 +228,7 @@ def on_exec(self, event: wx.Event): event.Skip(False) def set_output_vmd_path(self, event, is_force=False): - self.file_set.set_output_vmd_path(event) + self.file_set.set_output_vmd_path(event, is_force) # カメラ出力パスも一緒に変更する self.frame.camera_panel_ctrl.header_panel.set_output_vmd_path(event) diff --git a/src/module/MMath.pxd b/src/module/MMath.pxd index 462e2d7..3dc7f68 100644 --- a/src/module/MMath.pxd +++ b/src/module/MMath.pxd @@ -54,6 +54,42 @@ cdef class MVector2D: cpdef effective(self) + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] add_MVector2D(self, MVector2D other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] add_float(self, DTYPE_FLOAT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] add_int(self, DTYPE_INT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] sub_MVector2D(self, MVector2D other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] sub_float(self, DTYPE_FLOAT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] sub_int(self, DTYPE_INT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mul_MVector2D(self, MVector2D other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mul_float(self, DTYPE_FLOAT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mul_int(self, DTYPE_INT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] truediv_MVector2D(self, MVector2D other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] truediv_float(self, DTYPE_FLOAT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] truediv_int(self, DTYPE_INT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] floordiv_MVector2D(self, MVector2D other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] floordiv_float(self, DTYPE_FLOAT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] floordiv_int(self, DTYPE_INT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mod_MVector2D(self, MVector2D other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mod_float(self, DTYPE_FLOAT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mod_int(self, DTYPE_INT_t other) + cdef class MVector3D: cdef np.ndarray __data @@ -90,6 +126,42 @@ cdef class MVector3D: cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] data(self) + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] add_MVector3D(self, MVector3D other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] add_float(self, DTYPE_FLOAT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] add_int(self, DTYPE_INT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] sub_MVector3D(self, MVector3D other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] sub_float(self, DTYPE_FLOAT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] sub_int(self, DTYPE_INT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mul_MVector3D(self, MVector3D other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mul_float(self, DTYPE_FLOAT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mul_int(self, DTYPE_INT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] truediv_MVector3D(self, MVector3D other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] truediv_float(self, DTYPE_FLOAT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] truediv_int(self, DTYPE_INT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] floordiv_MVector3D(self, MVector3D other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] floordiv_float(self, DTYPE_FLOAT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] floordiv_int(self, DTYPE_INT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mod_MVector3D(self, MVector3D other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mod_float(self, DTYPE_FLOAT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mod_int(self, DTYPE_INT_t other) + cpdef DTYPE_FLOAT_t x(self) cpdef DTYPE_FLOAT_t y(self) @@ -126,6 +198,42 @@ cdef class MVector4D: cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] data(self) + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] add_MVector4D(self, MVector4D other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] add_float(self, DTYPE_FLOAT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] add_int(self, DTYPE_INT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] sub_MVector4D(self, MVector4D other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] sub_float(self, DTYPE_FLOAT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] sub_int(self, DTYPE_INT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mul_MVector4D(self, MVector4D other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mul_float(self, DTYPE_FLOAT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mul_int(self, DTYPE_INT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] truediv_MVector4D(self, MVector4D other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] truediv_float(self, DTYPE_FLOAT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] truediv_int(self, DTYPE_INT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] floordiv_MVector4D(self, MVector4D other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] floordiv_float(self, DTYPE_FLOAT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] floordiv_int(self, DTYPE_INT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mod_MVector4D(self, MVector4D other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mod_float(self, DTYPE_FLOAT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mod_int(self, DTYPE_INT_t other) + cpdef DTYPE_FLOAT_t x(self) cpdef DTYPE_FLOAT_t y(self) @@ -238,6 +346,29 @@ cdef class MMatrix4x4: cpdef MVector3D mapVector(self, vector) cpdef MQuaternion toQuaternion(self) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=2] add_MMatrix4x4(self, MMatrix4x4 other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=2] add_float(self, DTYPE_FLOAT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=2] add_int(self, DTYPE_INT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=2] sub_MMatrix4x4(self, MMatrix4x4 other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=2] sub_float(self, DTYPE_FLOAT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=2] sub_int(self, DTYPE_INT_t other) + + cpdef MVector3D mul_MVector3D(self, MVector3D other) + + cpdef MVector4D mul_MVector4D(self, MVector4D other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=2] mul_MMatrix4x4(self, MMatrix4x4 other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=2] mul_float(self, DTYPE_FLOAT_t other) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=2] mul_int(self, DTYPE_INT_t other) + cpdef bint is_almost_null(v) diff --git a/src/module/MMath.pyx b/src/module/MMath.pyx index b827b4a..47a4e1b 100644 --- a/src/module/MMath.pyx +++ b/src/module/MMath.pyx @@ -83,76 +83,154 @@ cdef class MVector2D: return "MVector2D({0}, {1})".format(self.__data[0], self.__data[1]) def __lt__(self, other): - return np.all(self.data() < other.data()) + return np.all(np.less(self.data(), other.data())) def __le__(self, other): - return np.all(self.data() <= other.data()) + return np.all(np.less_equal(self.data(), other.data())) def __eq__(self, other): - return np.all(self.data() == other.data()) + return np.all(np.equal(self.data(), other.data())) def __ne__(self, other): - return np.any(self.data() != other.data()) + return np.any(np.not_equal(self.data(), other.data())) def __gt__(self, other): - return np.all(self.data() > other.data()) + return np.all(np.greater(self.data(), other.data())) def __ge__(self, other): - return np.all(self.data() >= other.data()) + return np.all(np.greater_equal(self.data(), other.data())) def __add__(self, other): - if isinstance(other, MVector2D): - v = self.data() + other.data() + if isinstance(other, np.float): + v = self.add_float(other) + elif isinstance(other, MVector2D): + v = self.add_MVector2D(other) + elif isinstance(other, np.int): + v = self.add_int(other) else: v = self.data() + other v2 = self.__class__(v) v2.effective() return v2 + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] add_MVector2D(self, MVector2D other): + return self.__data + other.__data + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] add_float(self, DTYPE_FLOAT_t other): + return self.__data + other + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] add_int(self, DTYPE_INT_t other): + return self.__data + other def __sub__(self, other): - if isinstance(other, MVector2D): - v = self.data() - other.data() + if isinstance(other, np.float): + v = self.sub_float(other) + elif isinstance(other, MVector2D): + v = self.sub_MVector2D(other) + elif isinstance(other, np.int): + v = self.sub_int(other) else: v = self.data() - other v2 = self.__class__(v) v2.effective() return v2 + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] sub_MVector2D(self, MVector2D other): + return self.__data - other.__data + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] sub_float(self, DTYPE_FLOAT_t other): + return self.__data - other + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] sub_int(self, DTYPE_INT_t other): + return self.__data - other def __mul__(self, other): - if isinstance(other, MVector2D): - v = self.data() * other.data() + if isinstance(other, np.float): + v = self.mul_float(other) + elif isinstance(other, MVector2D): + v = self.mul_MVector2D(other) + elif isinstance(other, np.int): + v = self.mul_int(other) else: v = self.data() * other v2 = self.__class__(v) v2.effective() return v2 + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mul_MVector2D(self, MVector2D other): + return self.__data * other.__data + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mul_float(self, DTYPE_FLOAT_t other): + return self.__data * other + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mul_int(self, DTYPE_INT_t other): + return self.__data * other def __truediv__(self, other): - if isinstance(other, MVector2D): - v = self.data() / other.data() + if isinstance(other, np.float): + v = self.truediv_float(other) + elif isinstance(other, MVector2D): + v = self.truediv_MVector2D(other) + elif isinstance(other, np.int): + v = self.truediv_int(other) else: v = self.data() / other v2 = self.__class__(v) v2.effective() return v2 + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] truediv_MVector2D(self, MVector2D other): + return self.__data / other.__data + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] truediv_float(self, DTYPE_FLOAT_t other): + return self.__data / other + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] truediv_int(self, DTYPE_INT_t other): + return self.__data / other def __floordiv__(self, other): - if isinstance(other, MVector2D): - v = self.data() // other.data() + if isinstance(other, np.float): + v = self.floordiv_float(other) + elif isinstance(other, MVector2D): + v = self.floordiv_MVector2D(other) + elif isinstance(other, np.int): + v = self.floordiv_int(other) else: v = self.data() // other v2 = self.__class__(v) v2.effective() return v2 + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] floordiv_MVector2D(self, MVector2D other): + return self.__data // other.__data + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] floordiv_float(self, DTYPE_FLOAT_t other): + return self.__data // other + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] floordiv_int(self, DTYPE_INT_t other): + return self.__data // other def __mod__(self, other): - if isinstance(other, MVector2D): - v = self.data() % other.data() + if isinstance(other, np.float): + v = self.mod_float(other) + elif isinstance(other, MVector2D): + v = self.mod_MVector2D(other) + elif isinstance(other, np.int): + v = self.mod_int(other) else: v = self.data() % other v2 = self.__class__(v) v2.effective() return v2 + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mod_MVector2D(self, MVector2D other): + return self.__data % other.__data + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mod_float(self, DTYPE_FLOAT_t other): + return self.__data % other + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mod_int(self, DTYPE_INT_t other): + return self.__data % other def __lshift__(self, other): if isinstance(other, MVector2D): @@ -191,14 +269,11 @@ cdef class MVector2D: return v2 def __neg__(self): - return self.__class__(-self.__data[0], -self.__data[1]) + return self.__class__(-self.x(), -self.y()) def __pos__(self): - return self.__class__(+self.__data[0], +self.__data[1]) + return self.__class__(+self.x(), +self.y()) - def __invert__(self): - return self.__class__(~self.__data[0], ~self.__data[1]) - cpdef DTYPE_FLOAT_t x(self): return self.__data[0] @@ -342,76 +417,158 @@ cdef class MVector3D: return "MVector3D({0}, {1}, {2})".format(self.__data[0], self.__data[1], self.__data[2]) def __lt__(self, other): - return np.all(self.data() < other.data()) + return np.all(np.less(self.data(), other.data())) def __le__(self, other): - return np.all(self.data() <= other.data()) + return np.all(np.less_equal(self.data(), other.data())) def __eq__(self, other): - return np.all(self.data() == other.data()) + cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] d1 = self.data() + cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] d2 = other.data() + return d1[0] == d2[0] and d1[1] == d2[1] and d1[2] == d2[2] def __ne__(self, other): - return np.any(self.data() != other.data()) + cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] d1 = self.data() + cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] d2 = other.data() + return d1[0] != d2[0] or d1[1] != d2[1] or d1[2] != d2[2] def __gt__(self, other): - return np.all(self.data() > other.data()) + return np.all(np.greater(self.data(), other.data())) def __ge__(self, other): - return np.all(self.data() >= other.data()) + return np.all(np.greater_equal(self.data(), other.data())) def __add__(self, other): - if isinstance(other, MVector3D): - v = self.data() + other.data() + if isinstance(other, np.float): + v = self.add_float(other) + elif isinstance(other, MVector3D): + v = self.add_MVector3D(other) + elif isinstance(other, np.int): + v = self.add_int(other) else: v = self.data() + other v2 = self.__class__(v) v2.effective() return v2 + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] add_MVector3D(self, MVector3D other): + return self.__data + other.__data + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] add_float(self, DTYPE_FLOAT_t other): + return self.__data + other + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] add_int(self, DTYPE_INT_t other): + return self.__data + other def __sub__(self, other): - if isinstance(other, MVector3D): - v = self.data() - other.data() + if isinstance(other, np.float): + v = self.sub_float(other) + elif isinstance(other, MVector3D): + v = self.sub_MVector3D(other) + elif isinstance(other, np.int): + v = self.sub_int(other) else: v = self.data() - other v2 = self.__class__(v) v2.effective() return v2 + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] sub_MVector3D(self, MVector3D other): + return self.__data - other.__data + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] sub_float(self, DTYPE_FLOAT_t other): + return self.__data - other + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] sub_int(self, DTYPE_INT_t other): + return self.__data - other def __mul__(self, other): - if isinstance(other, MVector3D): - v = self.data() * other.data() + if isinstance(other, np.float): + v = self.mul_float(other) + elif isinstance(other, MVector3D): + v = self.mul_MVector3D(other) + elif isinstance(other, np.int): + v = self.mul_int(other) else: v = self.data() * other v2 = self.__class__(v) v2.effective() return v2 + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mul_MVector3D(self, MVector3D other): + return self.__data * other.__data + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mul_float(self, DTYPE_FLOAT_t other): + return self.__data * other + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mul_int(self, DTYPE_INT_t other): + return self.__data * other def __truediv__(self, other): - if isinstance(other, MVector3D): - v = self.data() / other.data() + if isinstance(other, np.float): + v = self.truediv_float(other) + elif isinstance(other, MVector3D): + v = self.truediv_MVector3D(other) + elif isinstance(other, np.int): + v = self.truediv_int(other) else: v = self.data() / other v2 = self.__class__(v) v2.effective() return v2 + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] truediv_MVector3D(self, MVector3D other): + return self.__data / other.__data + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] truediv_float(self, DTYPE_FLOAT_t other): + return self.__data / other + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] truediv_int(self, DTYPE_INT_t other): + return self.__data / other def __floordiv__(self, other): - if isinstance(other, MVector3D): - v = self.data() // other.data() + if isinstance(other, np.float): + v = self.floordiv_float(other) + elif isinstance(other, MVector3D): + v = self.floordiv_MVector3D(other) + elif isinstance(other, np.int): + v = self.floordiv_int(other) else: v = self.data() // other v2 = self.__class__(v) v2.effective() return v2 + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] floordiv_MVector3D(self, MVector3D other): + return self.__data // other.__data + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] floordiv_float(self, DTYPE_FLOAT_t other): + return self.__data // other + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] floordiv_int(self, DTYPE_INT_t other): + return self.__data // other def __mod__(self, other): - if isinstance(other, MVector3D): - v = self.data() % other.data() + if isinstance(other, np.float): + v = self.mod_float(other) + elif isinstance(other, MVector3D): + v = self.mod_MVector3D(other) + elif isinstance(other, np.int): + v = self.mod_int(other) else: v = self.data() % other v2 = self.__class__(v) v2.effective() return v2 + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mod_MVector3D(self, MVector3D other): + return self.__data % other.__data + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mod_float(self, DTYPE_FLOAT_t other): + return self.__data % other + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mod_int(self, DTYPE_INT_t other): + return self.__data % other def __lshift__(self, other): if isinstance(other, MVector3D): @@ -450,14 +607,11 @@ cdef class MVector3D: return v2 def __neg__(self): - return self.__class__(-self.__data[0], -self.__data[1], -self.__data[2]) + return self.__class__(-self.x(), -self.y(), -self.z()) def __pos__(self): - return self.__class__(+self.__data[0], +self.__data[1], +self.__data[2]) + return self.__class__(+self.x(), +self.y(), +self.z()) - def __invert__(self): - return self.__class__(~self.__data[0], ~self.__data[1], ~self.__data[2]) - cpdef DTYPE_FLOAT_t x(self): return self.__data[0] @@ -540,76 +694,164 @@ cdef class MVector4D: return "MVector4D({0}, {1}, {2}, {3})".format(self.__data[0], self.__data[1], self.__data[2], self.__data[3]) def __lt__(self, other): - return np.all(self.data() < other.data()) + return self.data().less(other.data()) def __le__(self, other): - return np.all(self.data() <= other.data()) + return self.data().less_equal(other.data()) def __eq__(self, other): - return np.all(self.data() == other.data()) + cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] d1 = self.data() + cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] d2 = other.data() + return d1[0] == d2[0] and d1[1] == d2[1] and d1[2] == d2[2] and d1[3] == d2[3] def __ne__(self, other): - return np.any(self.data() != other.data()) + cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] d1 = self.data() + cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] d2 = other.data() + return d1[0] != d2[0] or d1[1] != d2[1] or d1[2] != d2[2] or d1[3] != d2[3] + + def __eq__(self, other): + return self.data().equal(other.data()) + + def __ne__(self, other): + return self.data().not_equal(other.data()) def __gt__(self, other): - return np.all(self.data() > other.data()) + return self.data().greater(other.data()) def __ge__(self, other): - return np.all(self.data() >= other.data()) + return self.data().greater_equal(other.data()) def __add__(self, other): - if isinstance(other, MVector4D): - v = self.data() + other.data() + if isinstance(other, np.float): + v = self.add_float(other) + elif isinstance(other, MVector4D): + v = self.add_MVector4D(other) + elif isinstance(other, np.int): + v = self.add_int(other) else: v = self.data() + other v2 = self.__class__(v) v2.effective() return v2 + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] add_MVector4D(self, MVector4D other): + return self.__data + other.__data + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] add_float(self, DTYPE_FLOAT_t other): + return self.__data + other + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] add_int(self, DTYPE_INT_t other): + return self.__data + other def __sub__(self, other): - if isinstance(other, MVector4D): - v = self.data() - other.data() + if isinstance(other, np.float): + v = self.sub_float(other) + elif isinstance(other, MVector4D): + v = self.sub_MVector4D(other) + elif isinstance(other, np.int): + v = self.sub_int(other) else: v = self.data() - other v2 = self.__class__(v) v2.effective() return v2 + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] sub_MVector4D(self, MVector4D other): + return self.__data - other.__data + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] sub_float(self, DTYPE_FLOAT_t other): + return self.__data - other + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] sub_int(self, DTYPE_INT_t other): + return self.__data - other def __mul__(self, other): - if isinstance(other, MVector4D): - v = self.data() * other.data() + if isinstance(other, np.float): + v = self.mul_float(other) + elif isinstance(other, MVector4D): + v = self.mul_MVector4D(other) + elif isinstance(other, np.int): + v = self.mul_int(other) else: v = self.data() * other v2 = self.__class__(v) v2.effective() return v2 + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mul_MVector4D(self, MVector4D other): + return self.__data * other.__data + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mul_float(self, DTYPE_FLOAT_t other): + return self.__data * other + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mul_int(self, DTYPE_INT_t other): + return self.__data * other def __truediv__(self, other): - if isinstance(other, MVector4D): - v = self.data() / other.data() + if isinstance(other, np.float): + v = self.truediv_float(other) + elif isinstance(other, MVector4D): + v = self.truediv_MVector4D(other) + elif isinstance(other, np.int): + v = self.truediv_int(other) else: v = self.data() / other v2 = self.__class__(v) v2.effective() return v2 + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] truediv_MVector4D(self, MVector4D other): + return self.__data / other.__data + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] truediv_float(self, DTYPE_FLOAT_t other): + return self.__data / other + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] truediv_int(self, DTYPE_INT_t other): + return self.__data / other def __floordiv__(self, other): - if isinstance(other, MVector4D): - v = self.data() // other.data() + if isinstance(other, np.float): + v = self.floordiv_float(other) + elif isinstance(other, MVector4D): + v = self.floordiv_MVector4D(other) + elif isinstance(other, np.int): + v = self.floordiv_int(other) else: v = self.data() // other v2 = self.__class__(v) v2.effective() return v2 + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] floordiv_MVector4D(self, MVector4D other): + return self.__data // other.__data + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] floordiv_float(self, DTYPE_FLOAT_t other): + return self.__data // other + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] floordiv_int(self, DTYPE_INT_t other): + return self.__data // other def __mod__(self, other): - if isinstance(other, MVector4D): - v = self.data() % other.data() + if isinstance(other, np.float): + v = self.mod_float(other) + elif isinstance(other, MVector4D): + v = self.mod_MVector4D(other) + elif isinstance(other, np.int): + v = self.mod_int(other) else: v = self.data() % other v2 = self.__class__(v) v2.effective() return v2 + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mod_MVector4D(self, MVector4D other): + return self.__data % other.__data + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mod_float(self, DTYPE_FLOAT_t other): + return self.__data % other + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mod_int(self, DTYPE_INT_t other): + return self.__data % other def __lshift__(self, other): if isinstance(other, MVector4D): @@ -648,14 +890,11 @@ cdef class MVector4D: return v2 def __neg__(self): - return self.__class__(-self.__data[0], -self.__data[1], -self.__data[2], -self.__data[3]) + return self.__class__(-self.x(), -self.y(), -self.z(), -self.w()) def __pos__(self): - return self.__class__(+self.__data[0], +self.__data[1], +self.__data[2], +self.__data[3]) + return self.__class__(+self.x(), +self.y(), +self.z(), +self.w()) - def __invert__(self): - return self.__class__(~self.__data[0], ~self.__data[1], ~self.__data[2], ~self.__data[3]) - cpdef DTYPE_FLOAT_t x(self): return self.__data[0] @@ -903,22 +1142,22 @@ cdef class MQuaternion: return np.quaternion(self.__data[0], self.__data[1], self.__data[2], self.__data[3]) def __lt__(self, other): - return np.all(self.data() < other.data()) + return self.data().less(other.data()) def __le__(self, other): - return np.all(self.data() <= other.data()) + return self.data().less_equal(other.data()) def __eq__(self, other): - return np.all(self.data() == other.data()) + return self.data().equal(other.data()) def __ne__(self, other): - return np.any(self.data() != other.data()) + return self.data().not_equal(other.data()) def __gt__(self, other): - return np.all(self.data() > other.data()) + return self.data().greater(other.data()) def __ge__(self, other): - return np.all(self.data() >= other.data()) + return self.data().greater_equal(other.data()) def __add__(self, other): if isinstance(other, MQuaternion): @@ -1322,72 +1561,117 @@ cdef class MMatrix4x4: return "MMatrix4x4({0})".format(self.data()) def __lt__(self, other): - return np.all(self.data() < other.data()) + return np.all(np.less(self.data(), other.data())) def __le__(self, other): - return np.all(self.data() <= other.data()) + return np.all(np.less_equal(self.data(), other.data())) def __eq__(self, other): - return np.all(self.data() == other.data()) + return np.all(np.equal(self.data(), other.data())) def __ne__(self, other): - return np.any(self.data() != other.data()) + return np.any(np.not_equal(self.data(), other.data())) def __gt__(self, other): - return np.all(self.data() > other.data()) + return np.all(np.greater(self.data(), other.data())) def __ge__(self, other): - return np.all(self.data() >= other.data()) + return np.all(np.greater_equal(self.data(), other.data())) def __add__(self, other): - if isinstance(other, MMatrix4x4): - v = self.data() + other.data() + if isinstance(other, np.float): + v = self.add_float(other) + elif isinstance(other, MMatrix4x4): + v = self.add_MMatrix4x4(other) + elif isinstance(other, np.int): + v = self.add_int(other) else: v = self.data() + other - return self.__class__(v) + v2 = self.__class__(v) + return v2 + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=2] add_MMatrix4x4(self, MMatrix4x4 other): + return self.__data + other.__data + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=2] add_float(self, DTYPE_FLOAT_t other): + return self.__data + other + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=2] add_int(self, DTYPE_INT_t other): + return self.__data + other def __sub__(self, other): - if isinstance(other, MMatrix4x4): - v = self.data() - other.data() + if isinstance(other, np.float): + v = self.sub_float(other) + elif isinstance(other, MMatrix4x4): + v = self.sub_MMatrix4x4(other) + elif isinstance(other, np.int): + v = self.sub_int(other) else: v = self.data() - other - return self.__class__(v) + v2 = self.__class__(v) + return v2 + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=2] sub_MMatrix4x4(self, MMatrix4x4 other): + return self.__data - other.__data + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=2] sub_float(self, DTYPE_FLOAT_t other): + return self.__data - other + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=2] sub_int(self, DTYPE_INT_t other): + return self.__data - other - # *演算子 def __mul__(self, other): - if isinstance(other, MVector3D): - d = self.data() - vec_mat = np.tile(np.array([other.x(), other.y(), other.z()]), (4, 1)) - data_sum = np.sum(vec_mat * d[:, :3], axis=1) + d[:, 3] - - x = data_sum[0] - y = data_sum[1] - z = data_sum[2] - w = data_sum[3] - - if w == 1.0: - return MVector3D(x, y, z) - elif w == 0.0: - return MVector3D() - else: - return MVector3D(x / w, y / w, z / w) + if isinstance(other, np.float): + v = self.mul_float(other) + elif isinstance(other, MMatrix4x4): + v = self.mul_MMatrix4x4(other) + elif isinstance(other, MVector3D): + return self.mul_MVector3D(other) elif isinstance(other, MVector4D): - vec_mat = np.tile(np.array([other.x(), other.y(), other.z(), other.w()]), (4, 1)) - data_sum = np.sum(vec_mat * self.data(), axis=1) + return self.mul_MVector4D(other) + elif isinstance(other, np.int): + v = self.mul_int(other) + else: + v = self.data() * other + v2 = self.__class__(v) + return v2 + + cpdef MVector3D mul_MVector3D(self, MVector3D other): + vec_mat = np.tile(np.array([other.x(), other.y(), other.z()]), (4, 1)) + data_sum = np.sum(vec_mat * self.__data[:, :3], axis=1) + self.__data[:, 3] + + x = data_sum[0] + y = data_sum[1] + z = data_sum[2] + w = data_sum[3] + + if w == 1.0: + return MVector3D(x, y, z) + elif w == 0.0: + return MVector3D() + else: + return MVector3D(x / w, y / w, z / w) - x = data_sum[0] - y = data_sum[1] - z = data_sum[2] - w = data_sum[3] + cpdef MVector4D mul_MVector4D(self, MVector4D other): + vec_mat = np.tile(np.array([other.x(), other.y(), other.z(), other.w()]), (4, 1)) + data_sum = np.sum(vec_mat * self.__data, axis=1) + + x = data_sum[0] + y = data_sum[1] + z = data_sum[2] + w = data_sum[3] + + return MVector4D(x, y, z, w) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=2] mul_MMatrix4x4(self, MMatrix4x4 other): + return np.dot(self.data(), other.data()) + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=2] mul_float(self, DTYPE_FLOAT_t other): + return self.__data * other + + cpdef np.ndarray[DTYPE_FLOAT_t, ndim=2] mul_int(self, DTYPE_INT_t other): + return self.__data * other - return MVector4D(x, y, z, w) - elif isinstance(other, MMatrix4x4): - v = np.dot(self.data(), other.data()) - return self.__class__(v) - - v = self.data() * other - return self.__class__(v) - def __iadd__(self, other): self.__data = self.data() + other.data().T return self diff --git a/vmdising_np64.spec b/vmdising_np64.spec index 25e344e..bba2b98 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,7 +26,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β45_64bit', + name='VmdSizing_5.01_β46_64bit', debug=False, bootloader_ignore_signals=False, strip=False, From 1e438de49ac73ec50c2036b865ce95f4384069f6 Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Sat, 26 Sep 2020 21:11:07 +0900 Subject: [PATCH 13/37] =?UTF-8?q?5.01=5F=CE=B247?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VmdSizing_5.01_β47 miumiu ・数学ライブラリ  ・fromDirectionのメソッド呼び出しが間違っていたので修正  ・影響範囲:上半身補正、下半身補正 --- src/executor.py | 2 +- src/module/MMath.pyx | 2 +- vmdising_np64.spec | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/executor.py b/src/executor.py index 1dd9597..254ef26 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00_β46" +VERSION_NAME = "ver5.00_β47" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/module/MMath.pyx b/src/module/MMath.pyx index 47a4e1b..532a048 100644 --- a/src/module/MMath.pyx +++ b/src/module/MMath.pyx @@ -1283,7 +1283,7 @@ cdef MQuaternion fromAxisAndQuaternion(vec3, qq): return MQuaternion(c, x * s, y * s, z * s).normalized() cdef MQuaternion fromDirection(direction, up): - if direction.is_almost_null: + if direction.is_almost_null(): return MQuaternion() zAxis = direction.normalized() diff --git a/vmdising_np64.spec b/vmdising_np64.spec index bba2b98..e6324ff 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,7 +26,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β46_64bit', + name='VmdSizing_5.01_β47_64bit', debug=False, bootloader_ignore_signals=False, strip=False, From 5cb774c5730b8cd9eba2ea6c0274acadbd2fbeef Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Sat, 26 Sep 2020 22:20:58 +0900 Subject: [PATCH 14/37] =?UTF-8?q?5.01=5F=CE=B248?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VmdSizing_5.01_β48 miumiu ・数学ライブラリ  ・型宣言追加 --- src/executor.py | 2 +- src/module/MMath.pxd | 44 ++--- src/module/MMath.pyx | 391 ++++++++++++++++++++++--------------------- vmdising_np64.spec | 2 +- 4 files changed, 220 insertions(+), 219 deletions(-) diff --git a/src/executor.py b/src/executor.py index 254ef26..74820e9 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00_β47" +VERSION_NAME = "ver5.00_β48" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/module/MMath.pxd b/src/module/MMath.pxd index 3dc7f68..bce033c 100644 --- a/src/module/MMath.pxd +++ b/src/module/MMath.pxd @@ -104,11 +104,11 @@ cdef class MVector3D: cpdef normalize(self) - cpdef double distanceToPoint(self, v) + cpdef double distanceToPoint(self, MVector3D v) - cpdef MVector3D project(self, modelView, projection, viewport) + cpdef MVector3D project(self, MMatrix4x4 modelView, MMatrix4x4 projection, MRect viewport) - cpdef MVector3D unproject(self, modelView, projection, viewport) + cpdef MVector3D unproject(self, MMatrix4x4 modelView, MMatrix4x4 projection, MRect viewport) cpdef MVector4D toVector4D(self) @@ -174,9 +174,9 @@ cdef class MVector3D: cpdef setZ(self, z) -cdef MVector3D crossProduct_MVector3D(v1, v2) +cdef MVector3D crossProduct_MVector3D(MVector3D v1, MVector3D v2) -cdef double dotProduct_MVector3D(v1, v2) +cdef double dotProduct_MVector3D(MVector3D v1, MVector3D v2) cdef class MVector4D: @@ -250,7 +250,7 @@ cdef class MVector4D: cpdef setW(self, w) -cdef double dotProduct_MVector4D(v1, v2) +cdef double dotProduct_MVector4D(MVector4D v1, MVector4D v2) cdef class MQuaternion: cdef np.ndarray __data @@ -279,7 +279,7 @@ cdef class MQuaternion: cpdef double toDegree(self) - cpdef double calcTheata(self, v) + cpdef double calcTheata(self, MQuaternion v) cpdef data(self) @@ -301,25 +301,25 @@ cdef class MQuaternion: cpdef setScalar(self, w) -cdef dotProduct_MQuaternion(v1, v2) +cdef double dotProduct_MQuaternion(MQuaternion v1, MQuaternion v2) -cdef MQuaternion fromAxisAndAngle(vec3, angle) +cdef MQuaternion fromAxisAndAngle(MVector3D vec3, double angle) -cdef MQuaternion fromAxisAndQuaternion(vec3, qq) +cdef MQuaternion fromAxisAndQuaternion(MVector3D vec3, MQuaternion qq) -cdef MQuaternion fromDirection(direction, up) +cdef MQuaternion fromDirection(MVector3D direction, MVector3D up) -cdef MQuaternion fromAxes(xAxis, yAxis, zAxis) +cdef MQuaternion fromAxes(MVector3D xAxis, MVector3D yAxis, MVector3D zAxis) -cdef MQuaternion fromRotationMatrix(rot3x3) +cdef MQuaternion fromRotationMatrix(np.ndarray[DTYPE_FLOAT_t, ndim=2] rot3x3) -cdef MQuaternion rotationTo(fromv, tov) +cdef MQuaternion rotationTo(MVector3D fromv, MVector3D tov) -cdef MQuaternion fromEulerAngles(pitch, yaw, roll) +cdef MQuaternion fromEulerAngles(double pitch, double yaw, double roll) -cdef MQuaternion nlerp(q1, q2, t) +cdef MQuaternion nlerp(MQuaternion q1, MQuaternion q2, double t) -cdef MQuaternion slerp(q1, q2, t) +cdef MQuaternion slerp(MQuaternion q1, MQuaternion q2, double t) cdef class MMatrix4x4: @@ -333,17 +333,17 @@ cdef class MMatrix4x4: cpdef rotate(self, qq) - cpdef translate(self, vec3) + cpdef translate(self, MVector3D vec3) - cpdef scale(self, vec3) + cpdef scale(self, MVector3D vec3) cpdef setToIdentity(self) - cpdef lookAt(self, eye, center, up) + cpdef lookAt(self, MVector3D eye, MVector3D center, MVector3D up) - cpdef perspective(self, verticalAngle, aspectRatio, nearPlane, farPlane) + cpdef perspective(self, double verticalAngle, double aspectRatio, double nearPlane, double farPlane) - cpdef MVector3D mapVector(self, vector) + cpdef MVector3D mapVector(self, MVector3D vector) cpdef MQuaternion toQuaternion(self) diff --git a/src/module/MMath.pyx b/src/module/MMath.pyx index 532a048..ce49d52 100644 --- a/src/module/MMath.pyx +++ b/src/module/MMath.pyx @@ -67,8 +67,7 @@ cdef class MVector2D: cpdef normalize(self): cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] l2 = np.linalg.norm(self.data(), ord=2, axis=-1, keepdims=True) l2[l2 == 0] = 1 - cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] normv = self.data() / l2 - self.__data = normv + self.__data /= l2 cpdef effective(self): self.__data[np.isnan(self.data())] = 0 @@ -312,23 +311,22 @@ cdef class MVector3D: return float(np.linalg.norm(self.data(), ord=2)**2) cpdef MVector3D normalized(self): - l2 = np.linalg.norm(self.data(), ord=2, axis=-1, keepdims=True) + cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] l2 = np.linalg.norm(self.data(), ord=2, axis=-1, keepdims=True) l2[l2 == 0] = 1 - normv = self.data() / l2 + cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] normv = self.data() / l2 return MVector3D(normv[0], normv[1], normv[2]) cpdef normalize(self): self.effective() - l2 = np.linalg.norm(self.data(), ord=2, axis=-1, keepdims=True) + cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] l2 = np.linalg.norm(self.data(), ord=2, axis=-1, keepdims=True) l2[l2 == 0] = 1 - normv = self.data() / l2 - self.__data = normv + self.__data /= l2 - cpdef double distanceToPoint(self, v): + cpdef double distanceToPoint(self, MVector3D v): return MVector3D(self.data() - v.data()).length() - cpdef MVector3D project(self, modelView, projection, viewport): - tmp = MVector4D(self.x(), self.y(), self.z(), 1) + cpdef MVector3D project(self, MMatrix4x4 modelView, MMatrix4x4 projection, MRect viewport): + cdef MVector4D tmp = MVector4D(self.x(), self.y(), self.z(), 1) tmp = projection * modelView * tmp if is_almost_null(tmp.w()): tmp.setW(1) @@ -339,19 +337,18 @@ cdef class MVector3D: tmp.setY(tmp.y() * viewport.height() + viewport.y()) tmp.effective() - return tmp.toVector3D() - cpdef MVector3D unproject(self, modelView, projection, viewport): - inverse = (projection * modelView).inverted() + cpdef MVector3D unproject(self, MMatrix4x4 modelView, MMatrix4x4 projection, MRect viewport): + cdef MMatrix4x4 inverse = (projection * modelView).inverted() - tmp = MVector4D(self.x(), self.y(), self.z(), 1) + cdef MVector4D tmp = MVector4D(self.x(), self.y(), self.z(), 1) tmp.setX((tmp.x() - viewport.x()) / viewport.width()) tmp.setY((tmp.y() - viewport.y()) / viewport.height()) tmp = tmp * 2 - MVector4D(1, 1, 1, 1) tmp.effective() - obj = inverse * tmp + cdef MVector4D obj = inverse * tmp if is_almost_null(obj.w()): obj.setW(1) @@ -389,9 +386,9 @@ cdef class MVector3D: cpdef MVector3D non_zero(self): self.effective() - self.setX(0.0001 if is_almost_null(self.x()) else self.x()) - self.setY(0.0001 if is_almost_null(self.y()) else self.y()) - self.setZ(0.0001 if is_almost_null(self.z()) else self.z()) + self.setX(0.0000001 if is_almost_null(self.x()) else self.x()) + self.setY(0.0000001 if is_almost_null(self.y()) else self.y()) + self.setZ(0.0000001 if is_almost_null(self.z()) else self.z()) return self @@ -631,14 +628,13 @@ cdef class MVector3D: self.__data[2] = z -cdef MVector3D crossProduct_MVector3D(v1, v2): - crossv = np.cross(v1.data(), v2.data()) +cdef MVector3D crossProduct_MVector3D(MVector3D v1, MVector3D v2): + cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] crossv = np.cross(v1.data(), v2.data()) return MVector3D(crossv[0], crossv[1], crossv[2]) -cdef double dotProduct_MVector3D(v1, v2): - dotv = np.dot(v1.data(), v2.data()) - return dotv +cdef double dotProduct_MVector3D(MVector3D v1, MVector3D v2): + return np.dot(v1.data(), v2.data()) cdef class MVector4D: @@ -920,9 +916,8 @@ cdef class MVector4D: self.__data[3] = w -cdef double dotProduct_MVector4D(v1, v2): - dotv = np.dot(v1.data(), v2.data()) - return dotv +cdef double dotProduct_MVector4D(MVector4D v1, MVector4D v2): + return np.dot(v1.data(), v2.data()) cdef class MQuaternion: @@ -973,11 +968,11 @@ cdef class MQuaternion: self.setScalar(1 if self.scalar() == 0 else self.scalar()) cpdef MMatrix4x4 toMatrix4x4(self): - mat = MMatrix4x4() - m = mat.data() + cdef MMatrix4x4 mat = MMatrix4x4() + cdef np.ndarray[DTYPE_FLOAT_t, ndim=2] m = mat.data() # q(w,x,y,z)から(x,y,z,w)に並べ替え. - q2 = np.array([self.data().x, self.data().y, self.data().z, self.data().w], dtype=np.float64) + cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] q2 = np.array([self.data().x, self.data().y, self.data().z, self.data().w], dtype=np.float64) m[0, 0] = q2[3] * q2[3] + q2[0] * q2[0] - q2[1] * q2[1] - q2[2] * q2[2] m[0, 1] = 2.0 * q2[0] * q2[1] - 2.0 * q2[3] * q2[2] @@ -1009,27 +1004,27 @@ cdef class MQuaternion: cpdef MVector3D toEulerAngles4MMD(self): # MMDの表記に合わせたオイラー角 - euler = self.toEulerAngles() + cdef MVector3D euler = self.toEulerAngles() return MVector3D(euler.x(), -euler.y(), -euler.z()) # http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q37 cpdef MVector3D toEulerAngles(self): - xp = self.data().x - yp = self.data().y - zp = self.data().z - wp = self.data().w - - xx = xp * xp - xy = xp * yp - xz = xp * zp - xw = xp * wp - yy = yp * yp - yz = yp * zp - yw = yp * wp - zz = zp * zp - zw = zp * wp - lengthSquared = xx + yy + zz + wp * wp + cdef DTYPE_FLOAT_t xp = self.data().x + cdef DTYPE_FLOAT_t yp = self.data().y + cdef DTYPE_FLOAT_t zp = self.data().z + cdef DTYPE_FLOAT_t wp = self.data().w + + cdef DTYPE_FLOAT_t xx = xp * xp + cdef DTYPE_FLOAT_t xy = xp * yp + cdef DTYPE_FLOAT_t xz = xp * zp + cdef DTYPE_FLOAT_t xw = xp * wp + cdef DTYPE_FLOAT_t yy = yp * yp + cdef DTYPE_FLOAT_t yz = yp * zp + cdef DTYPE_FLOAT_t yw = yp * wp + cdef DTYPE_FLOAT_t zz = zp * zp + cdef DTYPE_FLOAT_t zw = zp * wp + cdef DTYPE_FLOAT_t lengthSquared = xx + yy + zz + wp * wp if not is_almost_null(lengthSquared - 1.0) and not is_almost_null(lengthSquared): xx /= lengthSquared @@ -1042,33 +1037,33 @@ cdef class MQuaternion: zz /= lengthSquared zw /= lengthSquared - pitch = math.asin(max(-1, min(1, -2.0 * (yz - xw)))) + cdef DTYPE_FLOAT_t pitch = asin(max(-1, min(1, -2.0 * (yz - xw)))) + cdef DTYPE_FLOAT_t yaw = 0 + cdef DTYPE_FLOAT_t roll = 0 - if pitch < (math.pi / 2): - if pitch > -(math.pi / 2): - yaw = math.atan2(2.0 * (xz + yw), 1.0 - 2.0 * (xx + yy)) - roll = math.atan2(2.0 * (xy + zw), 1.0 - 2.0 * (xx + zz)) + if pitch < (pi / 2): + if pitch > -(pi / 2): + yaw = atan2(2.0 * (xz + yw), 1.0 - 2.0 * (xx + yy)) + roll = atan2(2.0 * (xy + zw), 1.0 - 2.0 * (xx + zz)) else: # not a unique solution roll = 0.0 - yaw = -math.atan2(-2.0 * (xy - zw), 1.0 - 2.0 * (yy + zz)) + yaw = -atan2(-2.0 * (xy - zw), 1.0 - 2.0 * (yy + zz)) else: # not a unique solution roll = 0.0 - yaw = math.atan2(-2.0 * (xy - zw), 1.0 - 2.0 * (yy + zz)) - - v = MVector3D(math.degrees(pitch), math.degrees(yaw), math.degrees(roll)) + yaw = atan2(-2.0 * (xy - zw), 1.0 - 2.0 * (yy + zz)) - return v + return MVector3D(math.degrees(pitch), math.degrees(yaw), math.degrees(roll)) # 角度に変換 cpdef double toDegree(self): - return math.degrees(2 * math.acos(min(1, max(-1, self.scalar())))) + return math.degrees(2 * acos(min(1, max(-1, self.scalar())))) # 自分ともうひとつの値vとのtheta(変位量)を返す - cpdef double calcTheata(self, v): - dot = MQuaternion.dotProduct(self.normalized(), v.normalized()) - theta = math.acos(min(1, max(-1, dot))) + cpdef double calcTheata(self, MQuaternion v): + cdef double dot = MQuaternion.dotProduct(self.normalized(), v.normalized()) + cdef double theta = acos(min(1, max(-1, dot))) return theta @classmethod @@ -1076,11 +1071,11 @@ cdef class MQuaternion: return dotProduct_MQuaternion(v1, v2) @classmethod - def fromAxisAndAngle(cls, vec3: MVector3D, angle: float): + def fromAxisAndAngle(cls, vec3, angle): return fromAxisAndAngle(vec3, angle) @classmethod - def fromAxisAndQuaternion(cls, vec3: MVector3D, qq): + def fromAxisAndQuaternion(cls, vec3, qq): return fromAxisAndQuaternion(vec3, qq) @classmethod @@ -1241,115 +1236,120 @@ cdef class MQuaternion: return self.__class__(~self.data().w, ~self.data().x, ~self.data().y, ~self.data().z) -cdef dotProduct_MQuaternion(v1, v2): - dotv = np.sum(v1.data().components * v2.data().components) - return dotv +cdef double dotProduct_MQuaternion(MQuaternion v1, MQuaternion v2): + return np.sum(v1.data().components * v2.data().components) -cdef MQuaternion fromAxisAndAngle(vec3, angle): - x = vec3.x() - y = vec3.y() - z = vec3.z() - length = math.sqrt(x * x + y * y + z * z) +cdef MQuaternion fromAxisAndAngle(MVector3D vec3, double angle): + cdef DTYPE_FLOAT_t x = vec3.x() + cdef DTYPE_FLOAT_t y = vec3.y() + cdef DTYPE_FLOAT_t z = vec3.z() + cdef DTYPE_FLOAT_t length = sqrt(x * x + y * y + z * z) if not is_almost_null(length - 1.0) and not is_almost_null(length): x /= length y /= length z /= length - a = math.radians(angle / 2.0) - s = math.sin(a) - c = math.cos(a) + cdef DTYPE_FLOAT_t a = math.radians(angle / 2.0) + cdef DTYPE_FLOAT_t s = sin(a) + cdef DTYPE_FLOAT_t c = cos(a) return MQuaternion(c, x * s, y * s, z * s).normalized() -cdef MQuaternion fromAxisAndQuaternion(vec3, qq): +cdef MQuaternion fromAxisAndQuaternion(MVector3D vec3, MQuaternion qq): qq.normalize() - x = vec3.x() - y = vec3.y() - z = vec3.z() - length = math.sqrt(x * x + y * y + z * z) + cdef DTYPE_FLOAT_t x = vec3.x() + cdef DTYPE_FLOAT_t y = vec3.y() + cdef DTYPE_FLOAT_t z = vec3.z() + cdef DTYPE_FLOAT_t length = sqrt(x * x + y * y + z * z) if not is_almost_null(length - 1.0) and not is_almost_null(length): x /= length y /= length z /= length - a = math.acos(min(1, max(-1, qq.scalar()))) - s = math.sin(a) - c = math.cos(a) + cdef DTYPE_FLOAT_t a = acos(min(1, max(-1, qq.scalar()))) + cdef DTYPE_FLOAT_t s = sin(a) + cdef DTYPE_FLOAT_t c = cos(a) # logger.test("scalar: %s, a: %s, c: %s, degree: %s", qq.scalar(), a, c, math.degrees(2 * math.acos(min(1, max(-1, qq.scalar()))))) return MQuaternion(c, x * s, y * s, z * s).normalized() -cdef MQuaternion fromDirection(direction, up): +cdef MQuaternion fromDirection(MVector3D direction, MVector3D up): if direction.is_almost_null(): return MQuaternion() - zAxis = direction.normalized() - xAxis = MVector3D.crossProduct(up, zAxis) + cdef MVector3D zAxis = direction.normalized() + cdef MVector3D xAxis = crossProduct_MVector3D(up, zAxis) if (is_almost_null(xAxis.lengthSquared())): # collinear or invalid up vector derive shortest arc to new direction - return MQuaternion.rotationTo(MVector3D(0.0, 0.0, 1.0), zAxis) + return rotationTo(MVector3D(0.0, 0.0, 1.0), zAxis) xAxis.normalize() - yAxis = MVector3D.crossProduct(zAxis, xAxis) + cdef MVector3D yAxis = crossProduct_MVector3D(zAxis, xAxis) return MQuaternion.fromAxes(xAxis, yAxis, zAxis) -cdef MQuaternion fromAxes(xAxis, yAxis, zAxis): - rot3x3 = np.array([[xAxis.x(), yAxis.x(), zAxis.x()], [xAxis.y(), yAxis.y(), zAxis.y()], [xAxis.z(), yAxis.z(), zAxis.z()]]) - return MQuaternion.fromRotationMatrix(rot3x3) +cdef MQuaternion fromAxes(MVector3D xAxis, MVector3D yAxis, MVector3D zAxis): + return fromRotationMatrix(np.array([[xAxis.x(), yAxis.x(), zAxis.x()], [xAxis.y(), yAxis.y(), zAxis.y()], [xAxis.z(), yAxis.z(), zAxis.z()]], dtype=np.float64)) -cdef MQuaternion fromRotationMatrix(rot3x3): - scalar = 0 - axis = np.zeros(3) +cdef MQuaternion fromRotationMatrix(np.ndarray[DTYPE_FLOAT_t, ndim=2] rot3x3): + cdef DTYPE_FLOAT_t scalar = 0 + cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] axis = np.zeros(3) + + cdef DTYPE_FLOAT_t trace = rot3x3[0,0] + rot3x3[1,1] + rot3x3[2,2] + cdef DTYPE_FLOAT_t s = 0 + cdef np.ndarray[DTYPE_INT8_t, ndim=1] s_next + cdef int i = 0 + cdef int j = 0 + cdef int k = 0 - trace = rot3x3[0][0] + rot3x3[1][1] + rot3x3[2][2] if trace > 0.00000001: - s = 2.0 * math.sqrt(trace + 1.0) + s = 2.0 * sqrt(trace + 1.0) scalar = 0.25 * s - axis[0] = (rot3x3[2][1] - rot3x3[1][2]) / s - axis[1] = (rot3x3[0][2] - rot3x3[2][0]) / s - axis[2] = (rot3x3[1][0] - rot3x3[0][1]) / s + axis[0] = (rot3x3[2,1] - rot3x3[1,2]) / s + axis[1] = (rot3x3[0,2] - rot3x3[2,0]) / s + axis[2] = (rot3x3[1,0] - rot3x3[0,1]) / s else: s_next = np.array([1, 2, 0], dtype=np.int8) i = 0 - if rot3x3[1][1] > rot3x3[0][0]: + if rot3x3[1,1] > rot3x3[0,0]: i = 1 - if rot3x3[2][2] > rot3x3[i][i]: + if rot3x3[2,2] > rot3x3[i,i]: i = 2 j = s_next[i] k = s_next[j] - s = 2.0 * math.sqrt(rot3x3[i][i] - rot3x3[j][j] - rot3x3[k][k] + 1.0) + s = 2.0 * sqrt(rot3x3[i,i] - rot3x3[j,j] - rot3x3[k,k] + 1.0) axis[i] = 0.25 * s - scalar = (rot3x3[k][j] - rot3x3[j][k]) / s - axis[j] = (rot3x3[j][i] + rot3x3[i][j]) / s - axis[k] = (rot3x3[k][i] + rot3x3[i][k]) / s + scalar = (rot3x3[k,j] - rot3x3[j,k]) / s + axis[j] = (rot3x3[j,i] + rot3x3[i,j]) / s + axis[k] = (rot3x3[k,i] + rot3x3[i,k]) / s return MQuaternion(scalar, axis[0], axis[1], axis[2]) -cdef MQuaternion rotationTo(fromv, tov): - v0 = fromv.normalized() - v1 = tov.normalized() - d = MVector3D.dotProduct(v0, v1) + 1.0 +cdef MQuaternion rotationTo(MVector3D fromv, MVector3D tov): + cdef MVector3D v0 = fromv.normalized() + cdef MVector3D v1 = tov.normalized() + cdef double d = MVector3D.dotProduct(v0, v1) + 1.0 + cdef MVector3D axis # if dest vector is close to the inverse of source vector, ANY axis of rotation is valid if is_almost_null(d): - axis = MVector3D.crossProduct(MVector3D(1.0, 0.0, 0.0), v0) + axis = crossProduct_MVector3D(MVector3D(1.0, 0.0, 0.0), v0) if is_almost_null(axis.lengthSquared()): - axis = MVector3D.crossProduct(MVector3D(0.0, 1.0, 0.0), v0) + axis = crossProduct_MVector3D(MVector3D(0.0, 1.0, 0.0), v0) axis.normalize() # same as MQuaternion.fromAxisAndAngle(axis, 180.0) return MQuaternion(0.0, axis.x(), axis.y(), axis.z()).normalized() - d = math.sqrt(2.0 * d) - axis = MVector3D.crossProduct(v0, v1) / d + d = sqrt(2.0 * d) + axis = crossProduct_MVector3D(v0, v1) / d return MQuaternion(d * 0.5, axis.x(), axis.y(), axis.z()).normalized() -cdef MQuaternion fromEulerAngles(pitch, yaw, roll): +cdef MQuaternion fromEulerAngles(double pitch, double yaw, double roll): pitch = math.radians(pitch) yaw = math.radians(yaw) roll = math.radians(roll) @@ -1358,22 +1358,22 @@ cdef MQuaternion fromEulerAngles(pitch, yaw, roll): yaw *= 0.5 roll *= 0.5 - c1 = math.cos(yaw) - s1 = math.sin(yaw) - c2 = math.cos(roll) - s2 = math.sin(roll) - c3 = math.cos(pitch) - s3 = math.sin(pitch) - c1c2 = c1 * c2 - s1s2 = s1 * s2 - w = c1c2 * c3 + s1s2 * s3 - x = c1c2 * s3 + s1s2 * c3 - y = s1 * c2 * c3 - c1 * s2 * s3 - z = c1 * s2 * c3 - s1 * c2 * s3 + cdef double c1 = cos(yaw) + cdef double s1 = sin(yaw) + cdef double c2 = cos(roll) + cdef double s2 = sin(roll) + cdef double c3 = cos(pitch) + cdef double s3 = sin(pitch) + cdef double c1c2 = c1 * c2 + cdef double s1s2 = s1 * s2 + cdef double w = c1c2 * c3 + s1s2 * s3 + cdef double x = c1c2 * s3 + s1s2 * c3 + cdef double y = s1 * c2 * c3 - c1 * s2 * s3 + cdef double z = c1 * s2 * c3 - s1 * c2 * s3 return MQuaternion(w, x, y, z) -cdef MQuaternion nlerp(q1, q2, t): +cdef MQuaternion nlerp(MQuaternion q1, MQuaternion q2, double t): # Handle the easy cases first. if t <= 0.0: return q1 @@ -1381,16 +1381,16 @@ cdef MQuaternion nlerp(q1, q2, t): return q2 # Determine the angle between the two quaternions. - q2b = MQuaternion(q2.scalar(), q2.x(), q2.y(), q2.z()) + cdef MQuaternion q2b = MQuaternion(q2.scalar(), q2.x(), q2.y(), q2.z()) - dot = MQuaternion.dotProduct(q1, q2) + cdef double dot = dotProduct_MQuaternion(q1, q2) if dot < 0.0: q2b = -q2b # Perform the linear interpolation. return (q1 * (1.0 - t) + q2b * t).normalized() -cdef MQuaternion slerp(q1, q2, t): +cdef MQuaternion slerp(MQuaternion q1, MQuaternion q2, double t): # Handle the easy cases first. if t <= 0.0: return q1 @@ -1398,8 +1398,8 @@ cdef MQuaternion slerp(q1, q2, t): return q2 # Determine the angle between the two quaternions. - q2b = MQuaternion(q2.scalar(), q2.x(), q2.y(), q2.z()) - dot = MQuaternion.dotProduct(q1, q2) + cdef MQuaternion q2b = MQuaternion(q2.scalar(), q2.x(), q2.y(), q2.z()) + cdef double dot = dotProduct_MQuaternion(q1, q2) if dot < 0.0: q2b = -q2b @@ -1407,15 +1407,17 @@ cdef MQuaternion slerp(q1, q2, t): # Get the scale factors. If they are too small, # then revert to simple linear interpolation. - factor1 = 1.0 - t - factor2 = t + cdef double factor1 = 1.0 - t + cdef double factor2 = t + cdef double angle + cdef double sinOfAngle if (1.0 - dot) > 0.0000001: - angle = math.acos(max(0, min(1, dot))) - sinOfAngle = math.sin(angle) + angle = acos(max(0, min(1, dot))) + sinOfAngle = sin(angle) if sinOfAngle > 0.0000001: - factor1 = math.sin((1.0 - t) * angle) / sinOfAngle - factor2 = math.sin(t * angle) / sinOfAngle + factor1 = sin((1.0 - t) * angle) / sinOfAngle + factor2 = sin(t * angle) / sinOfAngle # Construct the result quaternion. return q1 * factor1 + q2b * factor2 @@ -1448,40 +1450,38 @@ cdef class MMatrix4x4: # 逆行列 cpdef MMatrix4x4 inverted(self): - v = np.linalg.inv(self.data()) - return MMatrix4x4(v) + return MMatrix4x4(np.linalg.inv(self.data())) # 回転行列 cpdef rotate(self, qq): - qq_mat = qq.toMatrix4x4() - self.__data = self.data().dot(qq_mat.data()) + self.__data = self.data().dot(qq.toMatrix4x4().data()) # 平行移動行列 - cpdef translate(self, vec3): - vec_mat = np.tile(np.array([vec3.x(), vec3.y(), vec3.z()]), (4, 1)) - data_mat = self.__data[:, :3] * vec_mat + cpdef translate(self, MVector3D vec3): + cdef np.ndarray[DTYPE_FLOAT_t, ndim=2] vec_mat = np.tile(np.array([vec3.x(), vec3.y(), vec3.z()]), (4, 1)) + cdef np.ndarray[DTYPE_FLOAT_t, ndim=2] data_mat = self.__data[:, :3] * vec_mat self.__data[:, 3] += np.sum(data_mat, axis=1) # 縮尺行列 - cpdef scale(self, vec3): - vec_mat = np.tile(np.array([vec3.x(), vec3.y(), vec3.z()]), (4, 1)) + cpdef scale(self, MVector3D vec3): + cdef np.ndarray[DTYPE_FLOAT_t, ndim=2] vec_mat = np.tile(np.array([vec3.x(), vec3.y(), vec3.z()]), (4, 1)) self.__data[:, :3] *= vec_mat # 単位行列 cpdef setToIdentity(self): self.__data = np.eye(4) - cpdef lookAt(self, eye, center, up): - forward = center - eye + cpdef lookAt(self, MVector3D eye, MVector3D center, MVector3D up): + cdef MVector3D forward = center - eye if forward.is_almost_null(): # ほぼ0の場合終了 return forward.normalize() - side = MVector3D.crossProduct(forward, up).normalized() - upVector = MVector3D.crossProduct(side, forward) + cdef MVector3D side = crossProduct_MVector3D(forward, up).normalized() + cdef MVector3D upVector = crossProduct_MVector3D(side, forward) - m = MMatrix4x4() + cdef MMatrix4x4 m = MMatrix4x4() m.__data[0, :-1] = side.data() m.__data[1, :-1] = upVector.data() m.__data[2, :-1] = -forward.data() @@ -1490,20 +1490,20 @@ cdef class MMatrix4x4: self *= m self.translate(-eye) - cpdef perspective(self, verticalAngle, aspectRatio, nearPlane, farPlane): + cpdef perspective(self, double verticalAngle, double aspectRatio, double nearPlane, double farPlane): if nearPlane == farPlane or aspectRatio == 0: return - radians = math.radians(verticalAngle / 2) - sine = math.sin(radians) + cdef double radians = math.radians(verticalAngle / 2) + cdef double sine = sin(radians) if sine == 0: return - cotan = math.cos(radians) / sine - clip = farPlane - nearPlane + cdef double cotan = cos(radians) / sine + cdef double clip = farPlane - nearPlane - m = MMatrix4x4() + cdef MMatrix4x4 m = MMatrix4x4() m.__data[0, 0] = cotan / aspectRatio m.__data[1, 1] = cotan m.__data[2, 2] = -(nearPlane + farPlane) / clip @@ -1512,47 +1512,48 @@ cdef class MMatrix4x4: self *= m - cpdef MVector3D mapVector(self, vector): - vec_mat = np.array([vector.x(), vector.y(), vector.z()]) - xyz = np.sum(vec_mat * self.__data[:3, :3], axis=1) + cpdef MVector3D mapVector(self, MVector3D vector): + cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] vec_mat = np.array([vector.x(), vector.y(), vector.z()]) + cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] xyz = np.sum(vec_mat * self.__data[:3, :3], axis=1) return MVector3D(xyz[0], xyz[1], xyz[2]) cpdef MQuaternion toQuaternion(self): - a = [[self.__data[0, 0], self.__data[0, 1], self.__data[0, 2], self.__data[0, 3]], - [self.__data[1, 0], self.__data[1, 1], self.__data[1, 2], self.__data[1, 3]], - [self.__data[2, 0], self.__data[2, 1], self.__data[2, 2], self.__data[2, 3]], - [self.__data[3, 0], self.__data[3, 1], self.__data[3, 2], self.__data[3, 3]]] - - q = MQuaternion() + cdef np.ndarray[DTYPE_FLOAT_t, ndim=2] a = np.array([[self.__data[0, 0], self.__data[0, 1], self.__data[0, 2], self.__data[0, 3]], + [self.__data[1, 0], self.__data[1, 1], self.__data[1, 2], self.__data[1, 3]], + [self.__data[2, 0], self.__data[2, 1], self.__data[2, 2], self.__data[2, 3]], + [self.__data[3, 0], self.__data[3, 1], self.__data[3, 2], self.__data[3, 3]]], dtype=np.float64) + cdef MQuaternion q = MQuaternion() + cdef DTYPE_FLOAT_t trace, s + # I removed + 1 - trace = a[0][0] + a[1][1] + a[2][2] + trace = a[0,0] + a[1,1] + a[2,2] # I changed M_EPSILON to 0 if trace > 0: - s = 0.5 / math.sqrt(trace + 1) + s = 0.5 / sqrt(trace + 1) q.setScalar(0.25 / s) - q.setX((a[2][1] - a[1][2]) * s) - q.setY((a[0][2] - a[2][0]) * s) - q.setZ((a[1][0] - a[0][1]) * s) + q.setX((a[2,1] - a[1,2]) * s) + q.setY((a[0,2] - a[2,0]) * s) + q.setZ((a[1,0] - a[0,1]) * s) else: - if a[0][0] > a[1][1] and a[0][0] > a[2][2]: - s = 2 * math.sqrt(1 + a[0][0] - a[1][1] - a[2][2]) - q.setScalar((a[2][1] - a[1][2]) / s) + if a[0,0] > a[1,1] and a[0,0] > a[2,2]: + s = 2 * sqrt(1 + a[0,0] - a[1,1] - a[2,2]) + q.setScalar((a[2,1] - a[1,2]) / s) q.setX(0.25 * s) - q.setY((a[0][1] + a[1][0]) / s) - q.setZ((a[0][2] + a[2][0]) / s) - elif a[1][1] > a[2][2]: - s = 2 * math.sqrt(1 + a[1][1] - a[0][0] - a[2][2]) - q.setScalar((a[0][2] - a[2][0]) / s) - q.setX((a[0][1] + a[1][0]) / s) + q.setY((a[0,1] + a[1,0]) / s) + q.setZ((a[0,2] + a[2,0]) / s) + elif a[1,1] > a[2,2]: + s = 2 * sqrt(1 + a[1,1] - a[0,0] - a[2,2]) + q.setScalar((a[0,2] - a[2,0]) / s) + q.setX((a[0,1] + a[1,0]) / s) q.setY(0.25 * s) - q.setZ((a[1][2] + a[2][1]) / s) + q.setZ((a[1,2] + a[2,1]) / s) else: - s = 2 * math.sqrt(1 + a[2][2] - a[0][0] - a[1][1]) - q.setScalar((a[1][0] - a[0][1]) / s) - q.setX((a[0][2] + a[2][0]) / s) - q.setY((a[1][2] + a[2][1]) / s) + s = 2 * sqrt(1 + a[2,2] - a[0,0] - a[1,1]) + q.setScalar((a[1,0] - a[0,1]) / s) + q.setX((a[0,2] + a[2,0]) / s) + q.setY((a[1,2] + a[2,1]) / s) q.setZ(0.25 * s) return q @@ -1637,13 +1638,13 @@ cdef class MMatrix4x4: return v2 cpdef MVector3D mul_MVector3D(self, MVector3D other): - vec_mat = np.tile(np.array([other.x(), other.y(), other.z()]), (4, 1)) - data_sum = np.sum(vec_mat * self.__data[:, :3], axis=1) + self.__data[:, 3] + cdef np.ndarray[DTYPE_FLOAT_t, ndim=2] vec_mat = np.tile(np.array([other.x(), other.y(), other.z()]), (4, 1)) + cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] data_sum = np.sum(vec_mat * self.__data[:, :3], axis=1) + self.__data[:, 3] - x = data_sum[0] - y = data_sum[1] - z = data_sum[2] - w = data_sum[3] + cdef DTYPE_FLOAT_t x = data_sum[0] + cdef DTYPE_FLOAT_t y = data_sum[1] + cdef DTYPE_FLOAT_t z = data_sum[2] + cdef DTYPE_FLOAT_t w = data_sum[3] if w == 1.0: return MVector3D(x, y, z) @@ -1653,13 +1654,13 @@ cdef class MMatrix4x4: return MVector3D(x / w, y / w, z / w) cpdef MVector4D mul_MVector4D(self, MVector4D other): - vec_mat = np.tile(np.array([other.x(), other.y(), other.z(), other.w()]), (4, 1)) - data_sum = np.sum(vec_mat * self.__data, axis=1) + cdef np.ndarray[DTYPE_FLOAT_t, ndim=2] vec_mat = np.tile(np.array([other.x(), other.y(), other.z(), other.w()]), (4, 1)) + cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] data_sum = np.sum(vec_mat * self.__data, axis=1) - x = data_sum[0] - y = data_sum[1] - z = data_sum[2] - w = data_sum[3] + cdef DTYPE_FLOAT_t x = data_sum[0] + cdef DTYPE_FLOAT_t y = data_sum[1] + cdef DTYPE_FLOAT_t z = data_sum[2] + cdef DTYPE_FLOAT_t w = data_sum[3] return MVector4D(x, y, z, w) diff --git a/vmdising_np64.spec b/vmdising_np64.spec index e6324ff..99b965a 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,7 +26,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β47_64bit', + name='VmdSizing_5.01_β48_64bit', debug=False, bootloader_ignore_signals=False, strip=False, From cd5405db1da853dba9a57631ebf61c9f0b7c2d5a Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Sat, 26 Sep 2020 23:24:42 +0900 Subject: [PATCH 15/37] =?UTF-8?q?5.01=5F=CE=B249?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VmdSizing_5.01_β49 miumiu ・VMDデータライブラリ  ・読み込みのみCython化 --- src/executor.py | 2 +- src/mmd/VmdData.pxd | 40 ++++++++ src/mmd/{VmdData.py => VmdData.pyx} | 137 ++++++++++++++++++++++++---- src/module/MMath.pyx | 30 +++--- src/module/OneEuroFilter.py | 97 -------------------- src/setup_ext.py | 2 +- vmdising_np64.spec | 2 +- 7 files changed, 177 insertions(+), 133 deletions(-) create mode 100644 src/mmd/VmdData.pxd rename src/mmd/{VmdData.py => VmdData.pyx} (91%) delete mode 100644 src/module/OneEuroFilter.py diff --git a/src/executor.py b/src/executor.py index 74820e9..21c6123 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00_β48" +VERSION_NAME = "ver5.00_β49" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/mmd/VmdData.pxd b/src/mmd/VmdData.pxd new file mode 100644 index 0000000..23d45f7 --- /dev/null +++ b/src/mmd/VmdData.pxd @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# +from module.MMath cimport MRect, MVector2D, MVector3D, MVector4D, MQuaternion, MMatrix4x4 # noqa + +cdef class VmdBoneFrame: + cdef public str name + cdef public bytes bname + cdef public int fno + cdef public MVector3D position + cdef public MQuaternion rotation + cdef public MVector3D org_position + cdef public MQuaternion org_rotation + cdef public list interpolation + cdef public list org_interpolation + cdef public bint key + cdef public bint read + cdef public str avoidance + +cdef class VmdMotion: + cdef public str path + cdef public str signature + cdef public str model_name + cdef public int last_motion_frame + cdef public int motion_cnt + cdef public dict bones + cdef public int morph_cnt + cdef public dict morphs + cdef public int camera_cnt + cdef public dict cameras + cdef public int light_cnt + cdef public list lights + cdef public int shadow_cnt + cdef public list shadows + cdef public int ik_cnt + cdef public list showiks + cdef public str digest + + + + diff --git a/src/mmd/VmdData.py b/src/mmd/VmdData.pyx similarity index 91% rename from src/mmd/VmdData.py rename to src/mmd/VmdData.pyx index d822744..eb16ec2 100644 --- a/src/mmd/VmdData.py +++ b/src/mmd/VmdData.pyx @@ -4,20 +4,117 @@ import numpy as np import struct import _pickle as cPickle +from libc.math cimport pi, fabs -from module.OneEuroFilter import OneEuroFilter -from module.MMath import MRect, MVector2D, MVector3D, MVector4D, MQuaternion, MMatrix4x4 # noqa from utils import MBezierUtils # noqa from utils.MLogger import MLogger +from module.MMath import MRect, MVector2D, MVector3D, MVector4D, MQuaternion, MMatrix4x4 # noqa + logger = MLogger(__name__, level=1) -class VmdBoneFrame(): +# OneEuroFilter +# オリジナル:https://www.cristal.univ-lille.fr/~casiez/1euro/ +# ---------------------------------------------------------------------------- + +cdef class LowPassFilter: + + def __init__(self, alpha): + self.__setAlpha(alpha) + self.__y = -1 + self.__s = -1 + + cdef __setAlpha(self, double alpha): + alpha = max(0.000001, min(1, alpha)) + if alpha <= 0 or alpha > 1.0: + raise ValueError("alpha (%s) should be in (0.0, 1.0]" % alpha) + self.__alpha = alpha + + def __call__(self, value: double, timestamp=-1, alpha=-1): + return self.c__call__(value, timestamp, alpha) + + cdef double c__call__(self, double value, double timestamp, double alpha): + cdef double s = 0 + if alpha >= 0: + self.__setAlpha(alpha) + if self.__y < 0: + s = value + else: + s = self.__alpha * value + (1.0 - self.__alpha) * self.__s + self.__y = value + self.__s = s + return s + + cdef double lastValue(self): + return self.__y + + # IK用処理スキップ + cdef double skip(self, double value): + self.__y = value + self.__s = value + + return value + + +cdef class OneEuroFilter: + + def __init__(self, freq, mincutoff=1.0, beta=0.0, dcutoff=1.0): + if freq <= 0: + raise ValueError("freq should be >0") + if mincutoff <= 0: + raise ValueError("mincutoff should be >0") + if dcutoff <= 0: + raise ValueError("dcutoff should be >0") + self.__freq = freq + self.__mincutoff = mincutoff + self.__beta = beta + self.__dcutoff = dcutoff + self.__x = LowPassFilter(self.__alpha(self.__mincutoff)) + self.__dx = LowPassFilter(self.__alpha(self.__dcutoff)) + self.__lasttime = -1 + + cdef double __alpha(self, double cutoff): + cdef double te = 1.0 / self.__freq + cdef double tau = 1.0 / (2 * pi * cutoff) + return 1.0 / (1.0 + tau / te) + + def __call__(self, x: double, timestamp=-1): + return self.c__call__(x, timestamp) + + cdef double c__call__(self, double x, double timestamp): + # ---- update the sampling frequency based on timestamps + if self.__lasttime and timestamp: + self.__freq = 1.0 / (timestamp - self.__lasttime) + self.__lasttime = timestamp + # ---- estimate the current variation per second + cdef double prev_x = self.__x.lastValue() + cdef double dx = 0.0 if prev_x < 0 else (x - prev_x) * self.__freq # FIXME: 0.0 or value? + cdef double edx = self.__dx(dx, timestamp, alpha=self.__alpha(self.__dcutoff)) + # ---- use it to update the cutoff frequency + cdef double cutoff = self.__mincutoff + self.__beta * fabs(edx) + # ---- filter the given value + return self.__x(x, timestamp, alpha=self.__alpha(cutoff)) + + def skip(self, double x, timestamp=-1): + self.c_skip(x, timestamp) + + # IK用処理スキップ + cdef c_skip(self, double x, str timestamp): + # ---- update the sampling frequency based on timestamps + if self.__lasttime and timestamp and self.__lasttime != timestamp: + self.__freq = 1.0 / (timestamp - self.__lasttime) + self.__lasttime = timestamp + cdef double prev_x = self.__x.lastValue() + self.__dx.skip(prev_x) + self.__x.skip(x) + + +cdef class VmdBoneFrame: def __init__(self, fno=0): self.name = '' - self.bname = '' + self.bname = b'' self.fno = fno self.position = MVector3D() self.rotation = MQuaternion() @@ -34,7 +131,7 @@ def __init__(self, fno=0): def set_name(self, name): self.name = name - self.bname = '' if not name else name.encode('cp932').decode('shift_jis').encode('shift_jis')[:15].ljust(15, b'\x00') + self.bname = b'' if not name else name.encode('cp932').decode('shift_jis').encode('shift_jis')[:15].ljust(15, b'\x00') def copy(self): bf = VmdBoneFrame(self.fno) @@ -70,7 +167,7 @@ def write(self, fout): fout.write(bytearray([int(min(127, max(0, x))) for x in self.interpolation])) -class VmdMorphFrame(): +class VmdMorphFrame: def __init__(self, fno=0): self.name = '' self.bname = '' @@ -92,7 +189,7 @@ def __str__(self): return " 1.0: - raise ValueError("alpha (%s) should be in (0.0, 1.0]" % alpha) - self.__alpha = alpha - - def __call__(self, value, timestamp=None, alpha=None): - if alpha is not None: - self.__setAlpha(alpha) - if self.__y is None: - s = value - else: - s = self.__alpha * value + (1.0 - self.__alpha) * self.__s - self.__y = value - self.__s = s - return s - - def lastValue(self): - return self.__y - - # IK用処理スキップ - def skip(self, value): - self.__y = value - self.__s = value - - return value - - -# ---------------------------------------------------------------------------- -class OneEuroFilter(object): - - def __init__(self, freq, mincutoff=1.0, beta=0.0, dcutoff=1.0): - if freq <= 0: - raise ValueError("freq should be >0") - if mincutoff <= 0: - raise ValueError("mincutoff should be >0") - if dcutoff <= 0: - raise ValueError("dcutoff should be >0") - self.__freq = float(freq) - self.__mincutoff = float(mincutoff) - self.__beta = float(beta) - self.__dcutoff = float(dcutoff) - self.__x = LowPassFilter(self.__alpha(self.__mincutoff)) - self.__dx = LowPassFilter(self.__alpha(self.__dcutoff)) - self.__lasttime = None - - def __alpha(self, cutoff): - te = 1.0 / self.__freq - tau = 1.0 / (2 * math.pi * cutoff) - return 1.0 / (1.0 + tau / te) - - def __call__(self, x, timestamp=None): - # ---- update the sampling frequency based on timestamps - if self.__lasttime and timestamp: - self.__freq = 1.0 / (timestamp - self.__lasttime) - self.__lasttime = timestamp - # ---- estimate the current variation per second - prev_x = self.__x.lastValue() - dx = 0.0 if prev_x is None else (x - prev_x) * self.__freq # FIXME: 0.0 or value? - edx = self.__dx(dx, timestamp, alpha=self.__alpha(self.__dcutoff)) - # ---- use it to update the cutoff frequency - cutoff = self.__mincutoff + self.__beta * math.fabs(edx) - # ---- filter the given value - return self.__x(x, timestamp, alpha=self.__alpha(cutoff)) - - # IK用処理スキップ - def skip(self, x, timestamp=None): - # ---- update the sampling frequency based on timestamps - if self.__lasttime and timestamp and self.__lasttime != timestamp: - self.__freq = 1.0 / (timestamp - self.__lasttime) - self.__lasttime = timestamp - prev_x = self.__x.lastValue() - self.__dx.skip(prev_x) - self.__x.skip(x) - diff --git a/src/setup_ext.py b/src/setup_ext.py index 59b8b1f..28238c2 100644 --- a/src/setup_ext.py +++ b/src/setup_ext.py @@ -8,7 +8,7 @@ def get_ext(): ext = [] - sources = ["module\\MMath.pyx", "module\\MParams.py"] + sources = ["module\\MMath.pyx", "module\\MParams.py", "mmd\\VmdData.pyx", "mmd\\VmdReader.py"] # for path in glob.glob("*/**/*.pyx", recursive=True): # if os.path.isfile(path): # print(path) diff --git a/vmdising_np64.spec b/vmdising_np64.spec index 99b965a..0d8e581 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,7 +26,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β48_64bit', + name='VmdSizing_5.01_β49_64bit', debug=False, bootloader_ignore_signals=False, strip=False, From 011a90df3c722db93365c48286738bae56a972dd Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Sat, 26 Sep 2020 23:37:16 +0900 Subject: [PATCH 16/37] =?UTF-8?q?5.01=5F=CE=B250?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VmdSizing_5.01_β50 miumiu ・VMDデータライブラリ  ・まるっとCython状態に戻した --- src/executor.py | 2 +- src/mmd/VmdData.pxd | 36 +++ src/mmd/VmdData.pyx | 698 +++++++++++++++++++++++++++++++------------- vmdising_np64.spec | 2 +- 4 files changed, 539 insertions(+), 199 deletions(-) diff --git a/src/executor.py b/src/executor.py index 21c6123..03536ed 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00_β49" +VERSION_NAME = "ver5.00_β50" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/mmd/VmdData.pxd b/src/mmd/VmdData.pxd index 23d45f7..f75553a 100644 --- a/src/mmd/VmdData.pxd +++ b/src/mmd/VmdData.pxd @@ -35,6 +35,42 @@ cdef class VmdMotion: cdef public list showiks cdef public str digest + cdef c_regist_full_bf(self, int data_set_no, list bone_name_list, int offset) + + cdef list c_get_differ_fnos(self, int data_set_no, list bone_name_list, double limit_degrees, double limit_length) + + cdef c_smooth_bf(self, int data_set_no, str bone_name, bint is_rot, bint is_mov, double limit_degrees, int start_fno, int end_fno, bint is_show_log) + + cdef c_smooth_filter_bf(self, int data_set_no, str bone_name, bint is_rot, bint is_mov, int loop, dict config, int start_fno, int end_fno, bint is_show_log) + + cdef c_remove_unnecessary_bf(self, int data_set_no, str bone_name, bint is_rot, bint is_mov, \ + double offset, double rot_diff_limit, double mov_diff_limit, int start_fno, int end_fno, bint is_show_log, bint is_force) + + cdef c_regist_bf(self, VmdBoneFrame bf, str bone_name, int fno, bint copy_interpolation) + + cdef VmdBoneFrame c_calc_bf(self, str bone_name, int fno, bint is_key, bint is_read, bint is_reset_interpolation) + + cdef MQuaternion calc_bf_rot(self, VmdBoneFrame prev_bf, VmdBoneFrame fill_bf, VmdBoneFrame next_bf) + + cdef MVector3D calc_bf_pos(self, VmdBoneFrame prev_bf, VmdBoneFrame fill_bf, VmdBoneFrame next_bf) + + cdef bint split_bf_by_fno(self, str target_bone_name, VmdBoneFrame prev_bf, VmdBoneFrame next_bf, int fill_fno) + + cdef bint split_bf(self, str target_bone_name, VmdBoneFrame prev_bf, VmdBoneFrame next_bf) + + cdef int get_split_fill_fno(self, str target_bone_name, VmdBoneFrame prev_bf, VmdBoneFrame next_bf, \ + list x1_idxs, list y1_idxs, list x2_idxs, list y2_idxs) + + cdef reset_interpolation(self, str target_bone_name, VmdBoneFrame prev_bf, VmdBoneFrame now_bf, VmdBoneFrame next_bf, \ + list before_bz, list after_bz, list x1_idxs, list y1_idxs, list x2_idxs, list y2_idxs) + + cdef copy_interpolation(self, VmdBoneFrame org_bf, VmdBoneFrame rep_bf, str bz_type) + + cdef reset_interpolation_parts(self, str target_bone_name, VmdBoneFrame bf, list bzs, list x1_idxs, list y1_idxs, list x2_idxs, list y2_idxs) + + cpdef bint is_active_bones(self, str bone_name) + + diff --git a/src/mmd/VmdData.pyx b/src/mmd/VmdData.pyx index eb16ec2..0bdf1de 100644 --- a/src/mmd/VmdData.pyx +++ b/src/mmd/VmdData.pyx @@ -2,6 +2,9 @@ # import math import numpy as np +cimport numpy as np +cimport libc.math as cmath +from libcpp cimport list, str, int, float import struct import _pickle as cPickle from libc.math cimport pi, fabs @@ -13,6 +16,9 @@ from module.MMath import MRect, MVector2D, MVector3D, MVector4D, MQuaternion, MM logger = MLogger(__name__, level=1) +ctypedef np.int_t DTYPE_INT_t +ctypedef np.float64_t DTYPE_FLOAT_t + # OneEuroFilter # オリジナル:https://www.cristal.univ-lille.fr/~casiez/1euro/ @@ -298,77 +304,122 @@ cdef class VmdMotion: # ハッシュ値 self.digest = None - def regist_full_bf(self, data_set_no: int, bone_name_list: str, offset=1): + def regist_full_bf(self, data_set_no: int, bone_name_list: list, offset=1): + self.c_regist_full_bf(data_set_no, bone_name_list, offset) + + cdef c_regist_full_bf(self, int data_set_no, list bone_name_list, int offset): # 指定された全部のボーンのキーフレ取得 - fnos = self.get_bone_fnos(*bone_name_list) + cdef list fnos = self.get_bone_fnos(*bone_name_list) + + if len(fnos) == 0: + return + # オフセット単位でキーフレ計算 fnos.extend(x for x in range(fnos[-1])[::offset]) # 重複を除いて再計算 fnos = sorted(list(set(fnos))) + cdef str bone_name + cdef int fno, prev_sep_fno + cdef VmdBoneFrame bf + # 指定ボーン名でキーフレ登録 for bone_name in bone_name_list: prev_sep_fno = 0 for fno in fnos: - bf = self.calc_bf(bone_name, fno) - self.regist_bf(bf, bone_name, fno) + bf = self.c_calc_bf(bone_name, fno, is_key=False, is_read=False, is_reset_interpolation=False) + self.c_regist_bf(bf, bone_name, fno, copy_interpolation=False) + + if fno // 500 > prev_sep_fno and fnos[-1] > 0: + if data_set_no == 0: + logger.info("-- %sフレーム目:終了(%s%)【全打ち - %s】", fno, round((fno / fnos[-1]) * 100, 3), bone_name) + prev_sep_fno = fno // 500 + elif data_set_no > 0: + logger.info("-- %sフレーム目:終了(%s%)【No.%s - 全打ち - %s】", fno, round((fno / fnos[-1]) * 100, 3), data_set_no, bone_name) + prev_sep_fno = fno // 500 - if data_set_no > 0 and fno // 500 > prev_sep_fno and fnos[-1] > 0: - logger.info("-- %sフレーム目:終了(%s%)【No.%s - 全打ち - %s】", fno, round((fno / fnos[-1]) * 100, 3), data_set_no, bone_name) - prev_sep_fno = fno // 500 - def get_differ_fnos(self, data_set_no: int, bone_name_list: list, limit_degrees: float, limit_length: float): - limit_radians = math.cos(math.radians(limit_degrees)) - fnos = [0] - for bone_name in bone_name_list: - prev_sep_fno = 0 + return self.c_get_differ_fnos(data_set_no, bone_name_list, limit_degrees, limit_length) + + cdef list c_get_differ_fnos(self, int data_set_no, list bone_name_list, double limit_degrees, double limit_length): + # cdef double limit_radians = cmath.cos(math.radians(limit_degrees)) + cdef list fnos = [0] + cdef str bone_name + cdef int prev_sep_fno = 0 + cdef list bone_fnos + cdef VmdBoneFrame prev_bf + cdef int fno + cdef int last_fno + cdef VmdBoneFrame bf + cdef DTYPE_FLOAT_t dot + cdef DTYPE_FLOAT_t rot_diff, mov_diff - # 有効キーを取得 - bone_fnos = self.get_bone_fnos(bone_name, is_key=True) + prev_sep_fno = 0 - if len(bone_fnos) <= 0: - continue - - before_bf = self.calc_bf(bone_name, 0) # 比較対象bf - for fno in range(1, bone_fnos[-1]): - bf = self.calc_bf(bone_name, fno) + # 有効キーを取得 + bone_fnos = self.get_bone_fnos(*bone_name_list, is_key=True) + + if len(bone_fnos) <= 0: + return [] + + # 比較対象bf + rot_diff = 0 + mov_diff = 0 + prev_bf = None + last_fno = bone_fnos[-1] + 1 + for fno in range(1, last_fno + 1): + for bone_name in bone_name_list: + prev_bf = self.c_calc_bf(bone_name, fno - 1, is_key=False, is_read=False, is_reset_interpolation=False) + bf = self.c_calc_bf(bone_name, fno, is_key=False, is_read=False, is_reset_interpolation=False) if bf.read: # 読み込みキーである場合、必ず処理対象に追加 fnos.append(fno) - # 前回キーとして保持 - before_bf = bf.copy() + rot_diff = 0 + mov_diff = 0 else: # 読み込みキーではない場合、処理対象にするかチェック + if fno - 1 in fnos: + # 前のキーがある場合、とりあえずスルー + continue # 読み込みキーとの差 - dot = MQuaternion.dotProduct(before_bf.rotation, bf.rotation) - if dot < limit_radians: + rot_diff += abs(prev_bf.rotation.toDegree() - bf.rotation.toDegree()) + if rot_diff > limit_degrees and limit_degrees > 0: # 前と今回の内積の差が指定度数より離れている場合、追加 - logger.test("★ 追加 set: %s, %s, f: %s, dot: %s", data_set_no, bone_name, fno, dot) + logger.debug("★ 追加 set: %s, %s, f: %s, diff: %s", data_set_no, bone_name, fno, rot_diff) fnos.append(fno) - # 前回キーとして保持 - before_bf = bf.copy() - - # 読み込みキーとの差 - diff = before_bf.position.distanceToPoint(bf.position) - if diff > limit_length: - # 前と今回の移動量の差が指定値より離れている場合、追加 - logger.test("★ 追加 set: %s, %s, f: %s, dot: %s", data_set_no, bone_name, fno, dot) - fnos.append(fno) - # 前回キーとして保持 - before_bf = bf.copy() + rot_diff = 0 + elif limit_length > 0: + # 読み込みキーとの差 + mov_diff += prev_bf.position.distanceToPoint(bf.position) + if mov_diff > limit_length: + # 前と今回の移動量の差が指定値より離れている場合、追加 + logger.test("★ 追加 set: %s, %s, f: %s, diff: %s", data_set_no, bone_name, fno, mov_diff) + fnos.append(fno) + mov_diff = 0 + else: + logger.test("× 追加なし set: %s, %s, f: %s, rot_diff: %s, mov_diff: %s", data_set_no, bone_name, fno, rot_diff, mov_diff) - if data_set_no > 0 and fno // 500 > prev_sep_fno and bone_fnos[-1] > 0: - logger.info("-- %sフレーム目:終了(%s%)【No.%s - キーフレ追加準備 - %s】", fno, round((fno / bone_fnos[-1]) * 100, 3), data_set_no, bone_name) - prev_sep_fno = fno // 500 + if fno // 2000 > prev_sep_fno and bone_fnos[-1] > 0: + if data_set_no > 0: + logger.info("-- %sフレーム目:終了(%s%)【No.%s - キーフレ追加準備 - %s】", fno, round((fno / bone_fnos[-1]) * 100, 3), data_set_no, bone_name) + prev_sep_fno = fno // 2000 + else: + logger.info("-- %sフレーム目:終了(%s%)【キーフレ追加準備 - %s】", fno, round((fno / bone_fnos[-1]) * 100, 3), bone_name) + prev_sep_fno = fno // 2000 # 重複を除いて再計算 return sorted(list(set(fnos))) # 指定ボーンが跳ねてたりするのを回避 - def smooth_bf(self, data_set_no: int, bone_name: str, is_rot: bool, is_mov: bool, limit_degrees: float, start_fno=-1, end_fno=-1, is_show_log=True): + def smooth_bf(self, data_set_no: int, bone_name: str, is_rot: bint, is_mov: bint, limit_degrees: float, start_fno=-1, end_fno=-1, is_show_log=True): + self.c_smooth_bf(data_set_no, bone_name, is_rot, is_mov, limit_degrees, start_fno, end_fno, is_show_log) + + cdef c_smooth_bf(self, int data_set_no, str bone_name, bint is_rot, bint is_mov, double limit_degrees, int start_fno, int end_fno, bint is_show_log): + cdef list fnos + # キーフレを取得する if start_fno < 0 and end_fno < 0: # 範囲指定がない場合、全範囲 @@ -376,13 +427,23 @@ cdef class VmdMotion: else: # 範囲指定がある場合はその範囲内だけ fnos = self.get_bone_fnos(bone_name, start_fno=start_fno, end_fno=end_fno) + + cdef double limit_radians = math.radians(limit_degrees) + + cdef int prev_sep_fno = 0 + cdef int fno + cdef VmdBoneFrame prev_bf + cdef VmdBoneFrame now_bf + cdef VmdBoneFrame next_bf + cdef DTYPE_FLOAT_t prev_next_dot + cdef DTYPE_FLOAT_t now_next_dot + cdef DTYPE_FLOAT_t diff - prev_sep_fno = 0 if len(fnos) > 2: for fno in fnos: - prev_bf = self.calc_bf(bone_name, fno - 1) - now_bf = self.calc_bf(bone_name, fno) - next_bf = self.calc_bf(bone_name, fno + 1) + prev_bf = self.c_calc_bf(bone_name, fno - 1, is_key=False, is_read=False, is_reset_interpolation=False) + now_bf = self.c_calc_bf(bone_name, fno, is_key=False, is_read=False, is_reset_interpolation=False) + next_bf = self.c_calc_bf(bone_name, fno + 1, is_key=False, is_read=False, is_reset_interpolation=False) if is_rot and now_bf.key: # 前後の内積 @@ -394,20 +455,41 @@ cdef class VmdMotion: logger.test("set: %s, %s, f: %s, diff: %s, prev_next_dot: %s, now_next_dot: %s", data_set_no, bone_name, fno, diff, prev_next_dot, now_next_dot) # 前後と自分の内積の差が一定以上の場合、円滑化 - if prev_next_dot > now_next_dot and diff > math.radians(limit_degrees): + if prev_next_dot > now_next_dot and diff > limit_radians: logger.debug("★ 円滑化 set: %s, %s, f: %s, diff: %s, prev_next_dot: %s, now_next_dot: %s", \ data_set_no, bone_name, fno, diff, prev_next_dot, now_next_dot) now_bf.rotation = MQuaternion.slerp(prev_bf.rotation, next_bf.rotation, ((now_bf.fno - prev_bf.fno) / (next_bf.fno - prev_bf.fno))) - if is_show_log and data_set_no > 0 and fno // 500 > prev_sep_fno and fnos[-1] > 0: + if is_show_log and data_set_no > 0 and fno // 2000 > prev_sep_fno and fnos[-1] > 0: logger.info("-- %sフレーム目:終了(%s%)【No.%s - 円滑化 - %s】", fno, round((fno / fnos[-1]) * 100, 3), data_set_no, bone_name) - prev_sep_fno = fno // 500 + prev_sep_fno = fno // 2000 - # フィルターをかける - def smooth_filter_bf(self, data_set_no: int, bone_name: str, is_rot: bool, is_mov: bool, loop=1, \ + def smooth_filter_bf(self, data_set_no: int, bone_name: str, is_rot: bint, is_mov: bint, loop=1, \ config={"freq": 30, "mincutoff": 0.3, "beta": 0.01, "dcutoff": 0.25}, start_fno=-1, end_fno=-1, is_show_log=True): - + self.c_smooth_filter_bf(data_set_no, bone_name, is_rot, is_mov, loop, config, start_fno, end_fno, is_show_log) + + # フィルターをかける + cdef c_smooth_filter_bf(self, int data_set_no, str bone_name, bint is_rot, bint is_mov, int loop, dict config, int start_fno, int end_fno, bint is_show_log): + cdef OneEuroFilter pxfilter + cdef OneEuroFilter pyfilter + cdef OneEuroFilter pzfilter + cdef OneEuroFilter rxfilter + cdef OneEuroFilter ryfilter + cdef OneEuroFilter rzfilter + cdef int n + cdef list fnos + cdef prev_sep_fno = 0 + cdef VmdBoneFrame now_bf + cdef double px + cdef double py + cdef double pz + cdef double rx + cdef double ry + cdef double rz + cdef MVector3D r + cdef MQuaternion new_qq + for n in range(loop): # 移動用フィルタ pxfilter = OneEuroFilter(**config) @@ -432,7 +514,7 @@ cdef class VmdMotion: # 全区間をフィルタにかける for fno in fnos: - now_bf = self.calc_bf(bone_name, fno) + now_bf = self.c_calc_bf(bone_name, fno, is_key=False, is_read=False, is_reset_interpolation=False) if is_mov: # 移動XYZそれぞれにフィルターをかける @@ -443,9 +525,7 @@ cdef class VmdMotion: if is_rot: # 回転XYZそれぞれにフィルターをかける(オイラー角) - now_qq = now_bf.rotation - - r = now_qq.toEulerAngles() + r = now_bf.rotation.toEulerAngles() rx = rxfilter(r.x(), fno) ry = ryfilter(r.y(), fno) rz = rzfilter(r.z(), fno) @@ -454,166 +534,362 @@ cdef class VmdMotion: new_qq = MQuaternion.fromEulerAngles(rx, ry, rz) now_bf.rotation = new_qq - if is_show_log and data_set_no > 0 and fno // 1000 > prev_sep_fno and fnos[-1] > 0: + if is_show_log and data_set_no > 0 and fno // 2000 > prev_sep_fno and fnos[-1] > 0: logger.info("-- %sフレーム目:終了(%s%)【No.%s - フィルタリング - %s(%s)】", fno, round((fno / fnos[-1]) * 100, 3), data_set_no, bone_name, (n + 1)) - prev_sep_fno = fno // 1000 + prev_sep_fno = fno // 2000 # 無効なキーを物理削除する def remove_unkey_bf(self, data_set_no: int, bone_name: str): for fno in self.get_bone_fnos(bone_name): - bf = self.calc_bf(bone_name, fno) + bf = self.c_calc_bf(bone_name, fno, is_key=False, is_read=False, is_reset_interpolation=False) if fno in self.bones[bone_name] and not bf.key: del self.bones[bone_name][fno] # 指定ボーンの不要キーを削除する - def remove_unnecessary_bf(self, data_set_no: int, bone_name: str, is_rot: bool, is_mov: bool, offset=0, start_fno=-1, end_fno=-1, is_show_log=True, is_force=False): - prev_sep_fno = 0 + # 変曲点を求める + # https://teratail.com/questions/162391 + def remove_unnecessary_bf(self, data_set_no: int, bone_name: str, is_rot: bint, is_mov: bint, \ + offset=0, rot_diff_limit=0.1, mov_diff_limit=0.1, start_fno=-1, end_fno=-1, is_show_log=True, is_force=False): + self.c_remove_unnecessary_bf(data_set_no, bone_name, is_rot, is_mov, offset, rot_diff_limit, mov_diff_limit, start_fno, end_fno, is_show_log, is_force) + + # 指定ボーンの不要キーを削除する + # 変曲点を求める + # https://teratail.com/questions/162391 + cdef c_remove_unnecessary_bf(self, int data_set_no, str bone_name, bint is_rot, bint is_mov, \ + double offset, double rot_diff_limit, double mov_diff_limit, int r_start_fno, int r_end_fno, bint is_show_log, bint is_force): + cdef int prev_sep_fno = 0 + cdef list fnos # キーフレを取得する - if start_fno < 0 and end_fno < 0: + if r_start_fno < 0 and r_end_fno < 0: # 範囲指定がない場合、全範囲 fnos = self.get_bone_fnos(bone_name) else: # 範囲指定がある場合はその範囲内だけ - fnos = self.get_bone_fnos(bone_name, start_fno=start_fno, end_fno=end_fno) + fnos = self.get_bone_fnos(bone_name, start_fno=r_start_fno, end_fno=r_end_fno) logger.debug("remove_unnecessary_bf prev: %s, %s", bone_name, len(fnos)) + + if len(fnos) <= 1: + return + + cdef int fno = 1 + cdef int start_fno = 0 + cdef list rot_values = [0] + cdef list mx_values = [0] + cdef list my_values = [0] + cdef list mz_values = [0] + cdef VmdBoneFrame bf = None + cdef VmdBoneFrame prev_bf = None + cdef VmdBoneFrame next_bf = None + cdef int f + cdef list rot_indices = [] + cdef list mx_indices = [] + cdef list my_indices = [] + cdef list mz_indices = [] + cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] rot_f_prime + cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] rot_sign + cdef np.ndarray[DTYPE_INT_t, ndim=1] rot_np_indices = np.array([], dtype=np.int) + cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mx_f_prime + cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mx_sign + cdef np.ndarray[DTYPE_INT_t, ndim=1] mx_np_indices = np.array([], dtype=np.int) + cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] my_f_prime + cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] my_sign + cdef np.ndarray[DTYPE_INT_t, ndim=1] my_np_indices = np.array([], dtype=np.int) + cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mz_f_prime + cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mz_sign + cdef np.ndarray[DTYPE_INT_t, ndim=1] mz_np_indices = np.array([], dtype=np.int) + cdef np.ndarray[DTYPE_INT_t, ndim=1] indices + cdef int inflection + cdef int inflection_fno + cdef list active_fnos + cdef bint is_inflection = False + cdef int last_fno = 0 + # cdef list joined_rot_bzs, joined_mx_bzs, joined_my_bzs, joined_mz_bzs, rot_inflection, mx_inflection, my_inflection, mz_inflection + + while fno <= fnos[-1]: + is_inflection = False + inflection_fno = start_fno + + bf = self.c_calc_bf(bone_name, fno, is_key=False, is_read=False, is_reset_interpolation=False) + logger.debug("*%s: f: %s, bf(%s):rot:%s", bone_name, fno, bf.fno, bf.rotation.toEulerAngles4MMD().to_log()) + + prev_bf = self.c_calc_bf(bone_name, start_fno, is_key=False, is_read=False, is_reset_interpolation=False) + logger.debug("*%s: f: %s, prev_bf(%s):rot:%s", bone_name, fno, prev_bf.fno, prev_bf.rotation.toEulerAngles4MMD().to_log()) + + # 変化量を保持 + rot_values.append(bf.rotation.calcTheata(prev_bf.rotation)) + mx_values.append(bf.position.x() - prev_bf.position.x()) + my_values.append(bf.position.y() - prev_bf.position.y()) + mz_values.append(bf.position.z() - prev_bf.position.z()) + + if 2 >= len(rot_values): + # 必要な個数が溜まるまでスルー + fno += 1 + continue - if len(fnos) > 2: - sfno = fnos[0] # 開始フレーム番号 - fno = fnos[1] # 次のフレーム番号 - fill_bfs = [] - for fidx, fno in enumerate(fnos): - prev_bf = self.calc_bf(bone_name, sfno) # 繋ぐ元のbf - now_bf = self.calc_bf(bone_name, fno) # 繋ぐ対象のbf - next_bf = self.calc_bf(bone_name, fno + 1) # 繋ぐ先のbf - is_next_key = next_bf.key # nextの有効有無 - - # 一旦登録 - self.regist_bf(now_bf, bone_name, fno) - - # 読み込みキーではない場合、結合を試す - logger.test("now: %s", now_bf) - - if (not now_bf.read or is_force) and fidx > 0: - # 現在キーを追加 - fill_bfs.append(now_bf) - - if self.join_bf(prev_bf, fill_bfs, next_bf, is_rot, is_mov, offset): - # 全ての補間曲線が繋ぐのに成功した場合、繋ぐ - logger.debug("f: %s, %s, ○補間曲線結合", fno, bone_name) - - # nowキーを物理的に削除 - if fno in self.bones[bone_name]: - del self.bones[bone_name][fno] - - # nextキーをキーの有効有無は問わずに登録 - self.regist_bf(next_bf, bone_name, fno + 1) - self.bones[bone_name][fno + 1].key = is_next_key - else: - logger.debug("f: %s, %s, ×補間曲線結合失敗", fno, bone_name) - # どれか失敗してたら、そのまま残す + # 単調増加としてキーを結合してみる + (joined_rot_bzs, rot_inflection) = MBezierUtils.join_value_2_bezier(fno, bone_name, rot_values, \ + offset=offset, diff_limit=rot_diff_limit) if is_rot else (True, []) + (joined_mx_bzs, mx_inflection) = MBezierUtils.join_value_2_bezier(fno, bone_name, mx_values, \ + offset=offset, diff_limit=mov_diff_limit) if is_mov else (True, []) + (joined_my_bzs, my_inflection) = MBezierUtils.join_value_2_bezier(fno, bone_name, my_values, \ + offset=offset, diff_limit=mov_diff_limit) if is_mov else (True, []) + (joined_mz_bzs, mz_inflection) = MBezierUtils.join_value_2_bezier(fno, bone_name, mz_values, \ + offset=offset, diff_limit=mov_diff_limit) if is_mov else (True, []) + + if joined_rot_bzs and joined_mx_bzs and joined_my_bzs and joined_mz_bzs: + next_bf = self.c_calc_bf(bone_name, fno, is_key=False, is_read=False, is_reset_interpolation=False) + # 結合できた場合、補間曲線をnextに設定 + if is_rot and len(joined_rot_bzs) > 0: + logger.debug("☆%s: f: %s, キー:回転補間曲線成功: 1: %s, 2: %s", bone_name, fno, joined_rot_bzs[1].to_log(), joined_rot_bzs[2].to_log()) + self.reset_interpolation_parts(bone_name, next_bf, joined_rot_bzs, MBezierUtils.R_x1_idxs, MBezierUtils.R_y1_idxs, MBezierUtils.R_x2_idxs, MBezierUtils.R_y2_idxs) + + if is_mov and len(joined_mx_bzs) > 0 and len(joined_my_bzs) > 0 and len(joined_mz_bzs) > 0: + logger.debug("☆%s: f: %s, キー:移動X補間曲線成功: 1: %s, 2: %s", bone_name, fno, joined_mx_bzs[1].to_log(), joined_mx_bzs[2].to_log()) + logger.debug("☆%s: f: %s, キー:移動Y補間曲線成功: 1: %s, 2: %s", bone_name, fno, joined_my_bzs[1].to_log(), joined_my_bzs[2].to_log()) + logger.debug("☆%s: f: %s, キー:移動Z補間曲線成功: 1: %s, 2: %s", bone_name, fno, joined_mz_bzs[1].to_log(), joined_mz_bzs[2].to_log()) + self.reset_interpolation_parts(bone_name, next_bf, joined_mx_bzs, MBezierUtils.MX_x1_idxs, MBezierUtils.MX_y1_idxs, MBezierUtils.MX_x2_idxs, MBezierUtils.MX_y2_idxs) + self.reset_interpolation_parts(bone_name, next_bf, joined_my_bzs, MBezierUtils.MY_x1_idxs, MBezierUtils.MY_y1_idxs, MBezierUtils.MY_x2_idxs, MBezierUtils.MY_y2_idxs) + self.reset_interpolation_parts(bone_name, next_bf, joined_mz_bzs, MBezierUtils.MZ_x1_idxs, MBezierUtils.MZ_y1_idxs, MBezierUtils.MZ_x2_idxs, MBezierUtils.MZ_y2_idxs) + + if bone_name in self.bones and fno in self.bones[bone_name]: + self.bones[bone_name][fno].key = True + self.bones[bone_name][fno].interpolation = next_bf.interpolation + logger.debug("**◇%s: f: %s, next_bf(%s):rot:%s", bone_name, fno, next_bf.fno, next_bf.rotation.toEulerAngles4MMD().to_log()) + else: + self.c_regist_bf(next_bf, bone_name, fno, copy_interpolation=True) + logger.debug("**☆%s: f: %s, next_bf(%s):rot:%s", bone_name, fno, next_bf.fno, next_bf.rotation.toEulerAngles4MMD().to_log()) + + for f in range(start_fno + 1, fno): + # 結合できた場合、区間内を削除 + if f in self.bones[bone_name]: + logger.debug("☆%s: f: %s, キー:キーフレ削除: df: %s", bone_name, fno, f) + self.bones[bone_name][f].key = False + + logger.debug("**☆有効キー %s: %s", bone_name, self.get_bone_fnos(bone_name, is_key=True)) + last_fno = fno - # nowキーを有効にする - now_bf.key = True + fno += 1 - sfno = fno # 開始を現在フレーム - fill_bfs = [] # 中間キーをクリア + # 変曲点処理は不要 + continue + else: + # 結合できなかった場合、変曲点チェックに移る + logger.debug("★%s: f: %s, キー:補間曲線失敗: rot_inflection: %s, mx_inflection: %s, my_inflection: %s, mz_inflection: %s", \ + bone_name, fno, rot_inflection, mx_inflection, my_inflection, mz_inflection) + + # 近似差分から変曲点を求める + # https://teratail.com/questions/162391 + rot_indices = [] + mx_indices = [] + my_indices = [] + mz_indices = [] + rot_np_values = np.ndarray([], dtype=np.float64) + mx_np_values = np.ndarray([], dtype=np.float64) + my_np_values = np.ndarray([], dtype=np.float64) + mz_np_values = np.ndarray([], dtype=np.float64) + if is_rot and len(rot_values) > 1: + rot_f_prime = np.gradient(rot_values) + rot_sign = np.concatenate([[0], np.diff(np.sign(rot_f_prime))]) + rot_np_indices = np.where(np.abs(rot_sign) > 1)[0].astype(np.int) + logger.debug("%s: f: %s, rot_values: %s", bone_name, fno, rot_values) + logger.debug("%s: f: %s, f_prime: %s", bone_name, fno, rot_f_prime) + logger.debug("%s: f: %s, sign: %s", bone_name, fno, rot_sign) + logger.debug("%s: f: %s, rot_np_indices: %s", bone_name, fno, rot_np_indices) + + if is_mov and len(mx_values) > 1: + mx_f_prime = np.gradient(mx_values) + mx_sign = np.concatenate([[0], [0], np.diff(np.sign(np.diff(mx_f_prime)))]) + mx_np_indices = np.where(np.abs(mx_sign) > 1)[0].astype(np.int) + logger.debug("%s: f: %s, mx_values: %s", bone_name, fno, mx_values) + logger.debug("%s: f: %s, f_prime: %s", bone_name, fno, mx_f_prime) + logger.debug("%s: f: %s, sign: %s", bone_name, fno, mx_sign) + logger.debug("%s: f: %s, mx_np_indices: %s", bone_name, fno, mx_np_indices) + + my_f_prime = np.gradient(my_values) + my_sign = np.concatenate([[0], [0], np.diff(np.sign(np.diff(my_f_prime)))]) + my_np_indices = np.where(np.abs(my_sign) > 1)[0].astype(np.int) + logger.debug("%s: f: %s, my_values: %s", bone_name, fno, my_values) + logger.debug("%s: f: %s, f_prime: %s", bone_name, fno, my_f_prime) + logger.debug("%s: f: %s, sign: %s", bone_name, fno, my_sign) + logger.debug("%s: f: %s, my_np_indices: %s", bone_name, fno, my_np_indices) + + mz_f_prime = np.gradient(mz_values) + mz_sign = np.concatenate([[0], [0], np.diff(np.sign(np.diff(mz_f_prime)))]) + mz_np_indices = np.where(np.abs(mz_sign) > 1)[0].astype(np.int) + logger.debug("%s: f: %s, mz_values: %s", bone_name, fno, mz_values) + logger.debug("%s: f: %s, f_prime: %s", bone_name, fno, mz_f_prime) + logger.debug("%s: f: %s, sign: %s", bone_name, fno, mz_sign) + logger.debug("%s: f: %s, mz_np_indices: %s", bone_name, fno, mz_np_indices) + + if rot_np_indices.size + mx_np_indices.size + my_np_indices.size + mz_np_indices.size > 0: + logger.debug("変曲点がある場合") + + # 不要なキーを連結する + # 変曲点があった場合、そこで区切る + indices = np.array([], dtype=np.int) + indices = np.append(indices, rot_np_indices) + indices = np.append(indices, mx_np_indices) + indices = np.append(indices, my_np_indices) + indices = np.append(indices, mz_np_indices) + + if indices.size > 0: + # 昇順に並べ替える + indices.sort() + logger.debug("indices: %s", indices) + # 変曲点で区切る + inflection = (indices[0]) + + if inflection <= 0: + fno += 1 + continue + + inflection_fno = start_fno + inflection + logger.debug("☆%s: 変曲点: %s, start_f: %s, f: %s, indices: %s", bone_name, inflection_fno, start_fno, fno, indices) + + next_bf = self.c_calc_bf(bone_name, inflection_fno, is_key=False, is_read=False, is_reset_interpolation=False) + + if inflection > 0: + # 結合したベジェ曲線 + (joined_rot_bzs, rot_inflection) = MBezierUtils.join_value_2_bezier(inflection_fno, bone_name, rot_values[:(inflection + 1)], \ + offset=offset, diff_limit=rot_diff_limit) if is_rot else (True, []) + (joined_mx_bzs, mx_inflection) = MBezierUtils.join_value_2_bezier(inflection_fno, bone_name, mx_values[:(inflection + 1)], \ + offset=offset, diff_limit=mov_diff_limit) if is_mov else (True, []) + (joined_my_bzs, my_inflection) = MBezierUtils.join_value_2_bezier(inflection_fno, bone_name, my_values[:(inflection + 1)], \ + offset=offset, diff_limit=mov_diff_limit) if is_mov else (True, []) + (joined_mz_bzs, mz_inflection) = MBezierUtils.join_value_2_bezier(inflection_fno, bone_name, mz_values[:(inflection + 1)], \ + offset=offset, diff_limit=mov_diff_limit) if is_mov else (True, []) + + if joined_rot_bzs and joined_mx_bzs and joined_my_bzs and joined_mz_bzs: + # 結合できた場合、補間曲線をnextに設定 + if is_rot: + logger.debug("☆%s: f: %s, 変曲点:回転補間曲線成功: 1: %s, 2: %s", bone_name, inflection_fno, joined_rot_bzs[1].to_log(), joined_rot_bzs[2].to_log()) + self.reset_interpolation_parts(bone_name, next_bf, joined_rot_bzs, MBezierUtils.R_x1_idxs, MBezierUtils.R_y1_idxs, MBezierUtils.R_x2_idxs, MBezierUtils.R_y2_idxs) + + if is_mov: + logger.debug("☆%s: f: %s, 変曲点:移動X補間曲線成功: 1: %s, 2: %s", bone_name, inflection_fno, joined_mx_bzs[1].to_log(), joined_mx_bzs[2].to_log()) + logger.debug("☆%s: f: %s, 変曲点:移動Y補間曲線成功: 1: %s, 2: %s", bone_name, inflection_fno, joined_my_bzs[1].to_log(), joined_my_bzs[2].to_log()) + logger.debug("☆%s: f: %s, 変曲点:移動Z補間曲線成功: 1: %s, 2: %s", bone_name, inflection_fno, joined_mz_bzs[1].to_log(), joined_mz_bzs[2].to_log()) + self.reset_interpolation_parts(bone_name, next_bf, joined_mx_bzs, MBezierUtils.MX_x1_idxs, MBezierUtils.MX_y1_idxs, MBezierUtils.MX_x2_idxs, MBezierUtils.MX_y2_idxs) + self.reset_interpolation_parts(bone_name, next_bf, joined_my_bzs, MBezierUtils.MY_x1_idxs, MBezierUtils.MY_y1_idxs, MBezierUtils.MY_x2_idxs, MBezierUtils.MY_y2_idxs) + self.reset_interpolation_parts(bone_name, next_bf, joined_mz_bzs, MBezierUtils.MZ_x1_idxs, MBezierUtils.MZ_y1_idxs, MBezierUtils.MZ_x2_idxs, MBezierUtils.MZ_y2_idxs) + + # 最終結合キーを置き換える + last_fno = inflection_fno + is_inflection = True + else: + # 結合できなかった場合、スルー + logger.debug("★%s: f: %s, 変曲点:補間曲線失敗: rot_inflection: %s, mx_inflection: %s, my_inflection: %s, mz_inflection: %s", \ + bone_name, inflection_fno, rot_inflection, mx_inflection, my_inflection, mz_inflection) + # 前回結合できたキーフレで置き換える + is_inflection = True + else: + # キーの結合に失敗して、かつ変曲点がない場合、前回結合できたキーフレの次に置き換える + logger.debug("キーの結合に失敗して、かつ変曲点がない場合") + last_fno += 1 + is_inflection = True + + if fno // 100 > prev_sep_fno and fnos[-1] > 0: + if data_set_no == 0: + logger.info("-- %sフレーム目:終了(%s%)【不要キー削除 - %s】", fno, round((fno / fnos[-1]) * 100, 3), bone_name) else: - logger.debug("f: %s, %s, ▲読み込みキー", fno, bone_name) - # 読み込み時のキーである場合、強制的に残す - sfno = fno # 開始を現在フレーム - fill_bfs = [] # 中間キーをクリア + logger.info("-- %sフレーム目:終了(%s%)【No.%s - 不要キー削除 - %s】", fno, round((fno / fnos[-1]) * 100, 3), data_set_no, bone_name) - if fno // 100 > prev_sep_fno: - if data_set_no == 0: - logger.info("-- %sフレーム目:終了(%s%)【不要キー削除 - %s】", fno, round((fno / fnos[-1]) * 100, 3), bone_name) - else: - logger.info("-- %sフレーム目:終了(%s%)【No.%s - 不要キー削除 - %s】", fno, round((fno / fnos[-1]) * 100, 3), data_set_no, bone_name) + prev_sep_fno = fno // 100 - prev_sep_fno = fno // 100 + # 変曲点で登録 + if next_bf: + if bone_name in self.bones and last_fno in self.bones[bone_name]: + self.bones[bone_name][last_fno].key = True + self.bones[bone_name][last_fno].interpolation = next_bf.interpolation + logger.debug("◇登録 %s: f: %s, next_bf(%s):rot:%s", bone_name, last_fno, next_bf.fno, next_bf.rotation.toEulerAngles4MMD().to_log()) + else: + self.c_regist_bf(next_bf, bone_name, last_fno, copy_interpolation=True) + logger.debug("☆登録 %s: f: %s, next_bf(%s):rot:%s", bone_name, last_fno, next_bf.fno, next_bf.rotation.toEulerAngles4MMD().to_log()) + + for f in range(start_fno + 1, last_fno): + # 結合できた場合、区間内を削除 + if f in self.bones[bone_name]: + logger.debug("☆%s: f: %s, 変曲点:キーフレ削除: df: %s", bone_name, last_fno, f) + self.bones[bone_name][f].key = False + + if is_inflection: + logger.debug("%s: f: %s, 開始キーフレ変更: start_fno: %s, fno: %s", bone_name, last_fno, start_fno, fno) + + # 開始キーフレは、変曲点までずらす + start_fno = last_fno + (0 if last_fno > start_fno else 1) + # fnoを変曲点まで戻す + fno = start_fno + + # 配列初期化 + rot_values = [0] + mx_values = [0] + my_values = [0] + mz_values = [0] + rot_np_values = np.ndarray([], dtype=np.float64) + mx_np_values = np.ndarray([], dtype=np.float64) + my_np_values = np.ndarray([], dtype=np.float64) + mz_np_values = np.ndarray([], dtype=np.float64) + + # 必ず進める + fno += 1 + + logger.debug("**☆有効キー(%s-%s) %s: %s", bone_name, start_fno, fno, self.get_bone_fnos(bone_name, is_key=True)) + + logger.debug("len: %s, last: %s", len(rot_values), fnos[-1]) - if start_fno < 0 and end_fno < 0: + if len(rot_values) == fnos[-1]: + # 最後まで変化がない場合、1F目以降を削除 + for f in range(1, fnos[-1] + 1): + if f in self.bones[bone_name]: + del self.bones[bone_name][f] + + if r_start_fno < 0 and r_end_fno < 0: # 範囲指定がない場合、全範囲 - active_fnos = self.get_bone_fnos(bone_name) + active_fnos = self.get_bone_fnos(bone_name, is_key=True) else: # 範囲指定がある場合はその範囲内だけ - active_fnos = self.get_bone_fnos(bone_name, start_fno=start_fno, end_fno=end_fno) + active_fnos = self.get_bone_fnos(bone_name, start_fno=r_start_fno, end_fno=r_end_fno, is_key=True) logger.debug("remove_unnecessary_bf after: %s, %s, all: %s", bone_name, active_fnos, len(fnos)) - - # 補間曲線込みでbfを結合できる場合、結合する - def join_bf(self, prev_bf: VmdBoneFrame, fill_bfs: list, next_bf: VmdBoneFrame, is_rot: bool, is_mov: bool, offset=0): - rot_values = [] - x_values = [] - y_values = [] - z_values = [] - - if is_rot: - rot_values = np.array([prev_bf.rotation.toDegree() * np.sign(prev_bf.rotation.x())] \ - + [bf.rotation.toDegree() * np.sign(bf.rotation.x()) for bf in fill_bfs] \ - + [next_bf.rotation.toDegree() * np.sign(next_bf.rotation.x())]) - (prev_bf.rotation.toDegree() * np.sign(next_bf.rotation.x())) - - logger.test("f: %s, %s, rot_values(): %s", prev_bf.fno, prev_bf.name, rot_values) - - if is_mov: - # X ------------ - x_values = np.array([prev_bf.position.x()] + [bf.position.x() for bf in fill_bfs] + [next_bf.position.x()]) - prev_bf.position.x() - - # Y ----------- - y_values = np.array([prev_bf.position.y()] + [bf.position.y() for bf in fill_bfs] + [next_bf.position.y()]) - prev_bf.position.y() - - # Z ----------- - z_values = np.array([prev_bf.position.z()] + [bf.position.z() for bf in fill_bfs] + [next_bf.position.z()]) - prev_bf.position.z() - - logger.test("f: %s, %s, x_values(): %s", prev_bf.fno, prev_bf.name, x_values) - logger.test("f: %s, %s, y_values(): %s", prev_bf.fno, prev_bf.name, y_values) - logger.test("f: %s, %s, z_values(): %s", prev_bf.fno, prev_bf.name, z_values) - - # 結合したベジェ曲線 - joined_rot_bzs = MBezierUtils.join_value_2_bezier(fill_bfs[-1].fno, next_bf.name, rot_values, offset=offset, diff_limit=0.1) if is_rot else True - joined_x_bzs = MBezierUtils.join_value_2_bezier(fill_bfs[-1].fno, next_bf.name, x_values, offset=offset, diff_limit=0.01) if is_mov else True - joined_y_bzs = MBezierUtils.join_value_2_bezier(fill_bfs[-1].fno, next_bf.name, y_values, offset=offset, diff_limit=0.01) if is_mov else True - joined_z_bzs = MBezierUtils.join_value_2_bezier(fill_bfs[-1].fno, next_bf.name, z_values, offset=offset, diff_limit=0.01) if is_mov else True - - if joined_rot_bzs and joined_x_bzs and joined_y_bzs and joined_z_bzs: - # 結合できた場合、補間曲線をnextに設定 - if is_rot: - self.reset_interpolation_parts(prev_bf.name, next_bf, joined_rot_bzs, MBezierUtils.R_x1_idxs, MBezierUtils.R_y1_idxs, MBezierUtils.R_x2_idxs, MBezierUtils.R_y2_idxs) - - if is_mov: - self.reset_interpolation_parts(prev_bf.name, next_bf, joined_x_bzs, MBezierUtils.MX_x1_idxs, MBezierUtils.MX_y1_idxs, MBezierUtils.MX_x2_idxs, MBezierUtils.MX_y2_idxs) - self.reset_interpolation_parts(prev_bf.name, next_bf, joined_y_bzs, MBezierUtils.MY_x1_idxs, MBezierUtils.MY_y1_idxs, MBezierUtils.MY_x2_idxs, MBezierUtils.MY_y2_idxs) - self.reset_interpolation_parts(prev_bf.name, next_bf, joined_z_bzs, MBezierUtils.MZ_x1_idxs, MBezierUtils.MZ_y1_idxs, MBezierUtils.MZ_x2_idxs, MBezierUtils.MZ_y2_idxs) - - return True - - # 結合できなかった場合、False - return False + + # 補間曲線分割ありで登録 + def regist_bf(self, bf: VmdBoneFrame, bone_name: str, fno: int, copy_interpolation=False): + self.c_regist_bf(bf, bone_name, fno, copy_interpolation) # 補間曲線分割ありで登録 - def regist_bf(self, bf: VmdBoneFrame, bone_name: str, fno: int): + cdef c_regist_bf(self, VmdBoneFrame bf, str bone_name, int fno, bint copy_interpolation): # 登録対象の場合のみ、補間曲線リセットで登録する - regist_bf = self.calc_bf(bone_name, fno, is_reset_interpolation=True) + cdef VmdBoneFrame regist_bf = self.c_calc_bf(bone_name, fno, is_key=False, is_read=False, is_reset_interpolation=True) regist_bf.position = bf.position.copy() regist_bf.rotation = bf.rotation.copy() regist_bf.org_position = bf.org_position.copy() regist_bf.org_rotation = bf.org_rotation.copy() + if copy_interpolation: + regist_bf.interpolation = cPickle.loads(cPickle.dumps(bf.interpolation, -1)) + # キーを登録 regist_bf.key = True self.bones[bone_name][fno] = regist_bf # 補間曲線を設定(有効なキーのみ) + cdef int prev_fno, next_fno prev_fno, next_fno = self.get_bone_prev_next_fno(bone_name, fno=fno, is_key=True) - prev_bf = self.calc_bf(bone_name, prev_fno) - next_bf = self.calc_bf(bone_name, next_fno) + cdef VmdBoneFrame prev_bf = self.c_calc_bf(bone_name, prev_fno, is_key=False, is_read=False, is_reset_interpolation=False) + cdef VmdBoneFrame next_bf = self.c_calc_bf(bone_name, next_fno, is_key=False, is_read=False, is_reset_interpolation=False) self.split_bf_by_fno(bone_name, prev_bf, next_bf, fno) # 補間曲線を考慮した指定フレーム番号の位置 # https://www55.atwiki.jp/kumiho_k/pages/15.html # https://harigane.at.webry.info/201103/article_1.html def calc_bf(self, bone_name: str, fno: int, is_key=False, is_read=False, is_reset_interpolation=False): - fill_bf = VmdBoneFrame(fno) + # cfun = profile(self.c_calc_bf) + # return cfun(bone_name, fno, is_key, is_read, is_reset_interpolation) + return self.c_calc_bf(bone_name, fno, is_key, is_read, is_reset_interpolation) + + cdef VmdBoneFrame c_calc_bf(self, str bone_name, int fno, bint is_key, bint is_read, bint is_reset_interpolation): + cdef VmdBoneFrame fill_bf = VmdBoneFrame(fno) if bone_name not in self.bones: self.bones[bone_name] = {fno: fill_bf} @@ -633,9 +909,9 @@ cdef class VmdMotion: return None # 番号より前のフレーム番号 - before_fnos = [x for x in sorted(self.bones[bone_name].keys()) if (x < fno)] + cdef list before_fnos = [x for x in sorted(self.bones[bone_name].keys()) if (x < fno)] # 番号より後のフレーム番号 - after_fnos = [x for x in sorted(self.bones[bone_name].keys()) if (x > fno)] + cdef list after_fnos = [x for x in sorted(self.bones[bone_name].keys()) if (x > fno)] if len(after_fnos) == 0 and len(before_fnos) == 0: fill_bf.set_name(bone_name) @@ -657,14 +933,18 @@ cdef class VmdMotion: fill_bf.read = False return fill_bf - prev_bf = self.bones[bone_name][before_fnos[-1]] - next_bf = self.bones[bone_name][after_fnos[0]] + cdef VmdBoneFrame prev_bf = self.bones[bone_name][before_fnos[-1]] + cdef VmdBoneFrame next_bf = self.bones[bone_name][after_fnos[0]] # 名前をコピー fill_bf.name = prev_bf.name fill_bf.bname = prev_bf.bname # 補間曲線を元に間を埋める + # rfunc = profile(self.calc_bf_rot) + # fill_bf.rotation = rfunc(prev_bf, fill_bf, next_bf) + # pfunc = profile(self.calc_bf_pos) + # fill_bf.position = pfunc(prev_bf, fill_bf, next_bf) fill_bf.rotation = self.calc_bf_rot(prev_bf, fill_bf, next_bf) fill_bf.position = self.calc_bf_pos(prev_bf, fill_bf, next_bf) @@ -705,7 +985,9 @@ cdef class VmdMotion: return fill_bf # 補間曲線を元に、回転ボーンの値を求める - def calc_bf_rot(self, prev_bf: VmdBoneFrame, fill_bf: VmdBoneFrame, next_bf: VmdBoneFrame): + cdef MQuaternion calc_bf_rot(self, VmdBoneFrame prev_bf, VmdBoneFrame fill_bf, VmdBoneFrame next_bf): + cdef double rx, ry, rt + if prev_bf.rotation != next_bf.rotation: # 回転補間曲線 rx, ry, rt = MBezierUtils.evaluate(next_bf.interpolation[MBezierUtils.R_x1_idxs[3]], next_bf.interpolation[MBezierUtils.R_y1_idxs[3]], \ @@ -716,7 +998,9 @@ cdef class VmdMotion: return prev_bf.rotation.copy() # 補間曲線を元に移動ボーンの値を求める - def calc_bf_pos(self, prev_bf: VmdBoneFrame, fill_bf: VmdBoneFrame, next_bf: VmdBoneFrame): + cdef MVector3D calc_bf_pos(self, VmdBoneFrame prev_bf, VmdBoneFrame fill_bf, VmdBoneFrame next_bf): + cdef double xx, xy, xt, yx, yy, yt, zx, zy, zt + cdef MVector3D fill_pos # 補間曲線を元に間を埋める if prev_bf.position != next_bf.position: @@ -744,18 +1028,18 @@ cdef class VmdMotion: return prev_bf.position.copy() # キーフレを指定されたフレーム番号の前後で分割する - def split_bf_by_fno(self, target_bone_name: str, prev_bf: VmdBoneFrame, next_bf: VmdBoneFrame, fill_fno: int): + cdef bint split_bf_by_fno(self, str target_bone_name, VmdBoneFrame prev_bf, VmdBoneFrame next_bf, int fill_fno): if not (prev_bf.fno < fill_fno < next_bf.fno): # 間の分割が出来ない場合、終了 return False # 補間曲線もともに分割する - fill_bf = self.calc_bf(target_bone_name, fill_fno, is_reset_interpolation=True) + cdef VmdBoneFrame fill_bf = self.c_calc_bf(target_bone_name, fill_fno, is_key=False, is_read=False, is_reset_interpolation=True) fill_bf.key = True self.bones[target_bone_name][fill_fno] = fill_bf # 分割結果 - fill_result = True + cdef bint fill_result = True # 前半の分割 fill_result = self.split_bf(target_bone_name, prev_bf, fill_bf) and fill_result # 後半の分割 @@ -764,25 +1048,26 @@ cdef class VmdMotion: return fill_result # キーフレを移動量の中心で分割する - def split_bf(self, target_bone_name: str, prev_bf: VmdBoneFrame, next_bf: VmdBoneFrame): + cdef bint split_bf(self, str target_bone_name, VmdBoneFrame prev_bf, VmdBoneFrame next_bf): if prev_bf.fno == next_bf.fno: # 間の分割が出来ない場合、終了 return True # 回転中点fno - r_fill_fno = self.get_split_fill_fno(target_bone_name, prev_bf, next_bf, \ - MBezierUtils.R_x1_idxs, MBezierUtils.R_y1_idxs, MBezierUtils.R_x2_idxs, MBezierUtils.R_y2_idxs) + cdef int r_fill_fno = self.get_split_fill_fno(target_bone_name, prev_bf, next_bf, \ + MBezierUtils.R_x1_idxs, MBezierUtils.R_y1_idxs, MBezierUtils.R_x2_idxs, MBezierUtils.R_y2_idxs) # 移動X中点fno - x_fill_fno = self.get_split_fill_fno(target_bone_name, prev_bf, next_bf, \ - MBezierUtils.MX_x1_idxs, MBezierUtils.MX_y1_idxs, MBezierUtils.MX_x2_idxs, MBezierUtils.MX_y2_idxs) + cdef int x_fill_fno = self.get_split_fill_fno(target_bone_name, prev_bf, next_bf, \ + MBezierUtils.MX_x1_idxs, MBezierUtils.MX_y1_idxs, MBezierUtils.MX_x2_idxs, MBezierUtils.MX_y2_idxs) # 移動Y中点fno - y_fill_fno = self.get_split_fill_fno(target_bone_name, prev_bf, next_bf, \ - MBezierUtils.MY_x1_idxs, MBezierUtils.MY_y1_idxs, MBezierUtils.MY_x2_idxs, MBezierUtils.MY_y2_idxs) + cdef int y_fill_fno = self.get_split_fill_fno(target_bone_name, prev_bf, next_bf, \ + MBezierUtils.MY_x1_idxs, MBezierUtils.MY_y1_idxs, MBezierUtils.MY_x2_idxs, MBezierUtils.MY_y2_idxs) # 移動Z中点fno - z_fill_fno = self.get_split_fill_fno(target_bone_name, prev_bf, next_bf, \ - MBezierUtils.MZ_x1_idxs, MBezierUtils.MZ_y1_idxs, MBezierUtils.MZ_x2_idxs, MBezierUtils.MZ_y2_idxs) + cdef int z_fill_fno = self.get_split_fill_fno(target_bone_name, prev_bf, next_bf, \ + MBezierUtils.MZ_x1_idxs, MBezierUtils.MZ_y1_idxs, MBezierUtils.MZ_x2_idxs, MBezierUtils.MZ_y2_idxs) - fnos = [] + cdef list fnos = [] + cdef int fill_fno for fill_fno in [r_fill_fno, x_fill_fno, y_fill_fno, z_fill_fno]: if fill_fno and prev_bf.fno < fill_fno < next_bf.fno: # fnoがあって範囲内の場合、設定対象でfnoを保持 @@ -791,7 +1076,7 @@ cdef class VmdMotion: # 重複なしの昇順リスト fnos = list(sorted(list(set(fnos)))) - fill_result = True + cdef bint fill_result = True if len(fnos) > 0: # 現在処理対象以外にfnoがある場合、その最小地点で前後に分割 fill_result = self.split_bf_by_fno(target_bone_name, prev_bf, next_bf, fnos[0]) and fill_result @@ -800,12 +1085,13 @@ cdef class VmdMotion: # キーフレを指定bf間の中間で区切れるフレーム番号を取得する # 分割が不要(範囲内に収まってる)場合、-1で対象外 - def get_split_fill_fno(self, target_bone_name: str, prev_bf: VmdBoneFrame, next_bf: VmdBoneFrame, \ - x1_idxs: list, y1_idxs: list, x2_idxs: list, y2_idxs: list): - next_x1v = next_bf.interpolation[x1_idxs[3]] - next_y1v = next_bf.interpolation[y1_idxs[3]] - next_x2v = next_bf.interpolation[x2_idxs[3]] - next_y2v = next_bf.interpolation[y2_idxs[3]] + cdef int get_split_fill_fno(self, str target_bone_name, VmdBoneFrame prev_bf, VmdBoneFrame next_bf, \ + list x1_idxs, list y1_idxs, list x2_idxs, list y2_idxs): + cdef int next_x1v = next_bf.interpolation[x1_idxs[3]] + cdef int next_y1v = next_bf.interpolation[y1_idxs[3]] + cdef int next_x2v = next_bf.interpolation[x2_idxs[3]] + cdef int next_y2v = next_bf.interpolation[y2_idxs[3]] + cdef int new_fill_fno if not MBezierUtils.is_fit_bezier_mmd([MVector2D(), MVector2D(next_x1v, next_y1v), MVector2D(next_x2v, next_y2v), MVector2D()]): # ベジェ曲線がMMDの範囲内に収まっていない場合、中点で分割 @@ -818,17 +1104,34 @@ cdef class VmdMotion: return -1 # 補間曲線の再設定処理 - def reset_interpolation(self, target_bone_name: str, prev_bf: VmdBoneFrame, now_bf: VmdBoneFrame, next_bf: VmdBoneFrame, \ - before_bz: list, after_bz: list, x1_idxs: list, y1_idxs: list, x2_idxs: list, y2_idxs: list): + cdef reset_interpolation(self, str target_bone_name, VmdBoneFrame prev_bf, VmdBoneFrame now_bf, VmdBoneFrame next_bf, \ + list before_bz, list after_bz, list x1_idxs, list y1_idxs, list x2_idxs, list y2_idxs): # 今回キーに設定 self.reset_interpolation_parts(target_bone_name, now_bf, before_bz, x1_idxs, y1_idxs, x2_idxs, y2_idxs) # nextキーに設定 self.reset_interpolation_parts(target_bone_name, next_bf, after_bz, x1_idxs, y1_idxs, x2_idxs, y2_idxs) + + # 補間曲線のコピー + cdef copy_interpolation(self, VmdBoneFrame org_bf, VmdBoneFrame rep_bf, str bz_type): + cdef list bz_x1_idxs, bz_y1_idxs, bz_x2_idxs, bz_y2_idxs + cdef list org_interpolation = cPickle.loads(cPickle.dumps(org_bf.interpolation, -1)) + + bz_x1_idxs, bz_y1_idxs, bz_x2_idxs, bz_y2_idxs = MBezierUtils.from_bz_type(bz_type) + + rep_bf.interpolation[bz_x1_idxs[0]] = rep_bf.interpolation[bz_x1_idxs[1]] = rep_bf.interpolation[bz_x1_idxs[2]] = rep_bf.interpolation[bz_x1_idxs[3]] \ + = org_interpolation[bz_x1_idxs[3]] + rep_bf.interpolation[bz_y1_idxs[0]] = rep_bf.interpolation[bz_y1_idxs[1]] = rep_bf.interpolation[bz_y1_idxs[2]] = rep_bf.interpolation[bz_y1_idxs[3]] \ + = org_interpolation[bz_y1_idxs[3]] + + rep_bf.interpolation[bz_x2_idxs[0]] = rep_bf.interpolation[bz_x2_idxs[2]] = rep_bf.interpolation[bz_x2_idxs[2]] = rep_bf.interpolation[bz_x2_idxs[3]] \ + = org_interpolation[bz_x2_idxs[3]] + rep_bf.interpolation[bz_y2_idxs[0]] = rep_bf.interpolation[bz_y2_idxs[2]] = rep_bf.interpolation[bz_y2_idxs[2]] = rep_bf.interpolation[bz_y2_idxs[3]] \ + = org_interpolation[bz_y2_idxs[3]] # 補間曲線の再設定部品 - def reset_interpolation_parts(self, target_bone_name: str, bf: VmdBoneFrame, bzs: list, x1_idxs: list, y1_idxs: list, x2_idxs: list, y2_idxs: list): + cdef reset_interpolation_parts(self, str target_bone_name, VmdBoneFrame bf, list bzs, list x1_idxs, list y1_idxs, list x2_idxs, list y2_idxs): # キーの始点は、B bf.interpolation[x1_idxs[0]] = bf.interpolation[x1_idxs[1]] = bf.interpolation[x1_idxs[2]] = bf.interpolation[x1_idxs[3]] = int(bzs[1].x()) bf.interpolation[y1_idxs[0]] = bf.interpolation[y1_idxs[1]] = bf.interpolation[y1_idxs[2]] = bf.interpolation[y1_idxs[3]] = int(bzs[1].y()) @@ -838,7 +1141,8 @@ cdef class VmdMotion: bf.interpolation[y2_idxs[0]] = bf.interpolation[y2_idxs[1]] = bf.interpolation[y2_idxs[2]] = bf.interpolation[y2_idxs[3]] = int(bzs[2].y()) # 有効なキーフレが入っているか - def is_active_bones(self, bone_name): + cpdef bint is_active_bones(self, str bone_name): + cdef VmdBoneFrame bf for bf in self.bones[bone_name].values(): if bf.position != MVector3D(): return True @@ -982,7 +1286,7 @@ cdef class VmdMotion: new_motion = VmdMotion() for bone_name in self.bones.keys(): - new_motion.bones[bone_name] = {fno: self.calc_bf(bone_name, fno).copy()} + new_motion.bones[bone_name] = {fno: self.c_calc_bf(bone_name, fno, is_key=False, is_read=False, is_reset_interpolation=False).copy()} return new_motion diff --git a/vmdising_np64.spec b/vmdising_np64.spec index 0d8e581..8abbf95 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,7 +26,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β49_64bit', + name='VmdSizing_5.01_β50_64bit', debug=False, bootloader_ignore_signals=False, strip=False, From 3a69d1dd4b09ad7fadb443869f61e0c7d48efd66 Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Sun, 27 Sep 2020 01:05:58 +0900 Subject: [PATCH 17/37] =?UTF-8?q?5.01=5F=CE=B251?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VmdSizing_5.01_β51 miumiu ・PMXデータライブラリ ・ベジェライブラリ  ・まるっとCython状態に戻した ・VMD読み込み処理でIKonoffの名称が他と持ち方が違っていたのを統一  → 出力時エラー修正 ・途中で停止した時に、時折進捗数欄に PmxData.pyx} | 1120 +++++++++-------- src/mmd/PmxReader.py | 75 +- src/mmd/VmdData.pxd | 22 + src/mmd/VmdData.pyx | 6 +- src/mmd/VmdReader.py | 13 +- src/mmd/VmdWriter.py | 2 +- src/module/MMath.pyx | 3 + src/service/SizingService.py | 8 +- src/service/parts/StanceService.py | 4 +- src/setup_ext.py | 4 +- src/utils/MBezierUtils.pxd | 33 + .../{MBezierUtils.py => MBezierUtils.pyx} | 266 ++-- vmdising_np64.spec | 2 +- 15 files changed, 1021 insertions(+), 694 deletions(-) create mode 100644 src/mmd/PmxData.pxd rename src/mmd/{PmxData.py => PmxData.pyx} (67%) create mode 100644 src/utils/MBezierUtils.pxd rename src/utils/{MBezierUtils.py => MBezierUtils.pyx} (69%) diff --git a/src/executor.py b/src/executor.py index 03536ed..f61afa6 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00_β50" +VERSION_NAME = "ver5.00_β51" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/mmd/PmxData.pxd b/src/mmd/PmxData.pxd new file mode 100644 index 0000000..1964acb --- /dev/null +++ b/src/mmd/PmxData.pxd @@ -0,0 +1,155 @@ +# -*- coding: utf-8 -*- +# + +from module.MMath cimport MRect, MVector2D, MVector3D, MVector4D, MQuaternion, MMatrix4x4 # noqa + + +cdef class Deform: + cdef public int index0 + + +cdef class Vertex: + cdef public int index + cdef public MVector3D position + cdef public MVector3D normal + cdef public list uv + cdef public list extended_uvs + cdef public Deform deform + cdef public float edge_factor + + +cdef class Ik: + cdef public int target_index + cdef public int loop + cdef public float limit_radian + cdef public list link + + +cdef class Bone: + cdef public str name + cdef public str english_name + cdef public MVector3D position + cdef public int parent_index + cdef public int layer + cdef public int flag + cdef public MVector3D tail_position + cdef public int tail_index + cdef public int effect_index + cdef public float effect_factor + cdef public MVector3D fixed_axis + cdef public MVector3D local_x_vector + cdef public MVector3D local_z_vector + cdef public int external_key + cdef public Ik ik + cdef public int index + cdef public bint display + + cdef public float len_1d + cdef public MVector3D len_3d + cdef public MVector3D local_offset + cdef public MVector3D global_ik_offset + + cdef public MVector3D ik_limit_min + cdef public MVector3D ik_limit_max + cdef public float dot_limit + cdef public float dot_near_limit + cdef public float dot_far_limit + cdef public float dot_single_limit + cdef public float degree_limit + + cdef public int BONEFLAG_TAILPOS_IS_BONE + cdef public int BONEFLAG_CAN_ROTATE + cdef public int BONEFLAG_CAN_TRANSLATE + cdef public int BONEFLAG_IS_VISIBLE + cdef public int BONEFLAG_CAN_MANIPULATE + cdef public int BONEFLAG_IS_IK + cdef public int BONEFLAG_IS_EXTERNAL_ROTATION + cdef public int BONEFLAG_IS_EXTERNAL_TRANSLATION + cdef public int BONEFLAG_HAS_FIXED_AXIS + cdef public int BONEFLAG_HAS_LOCAL_COORDINATE + cdef public int BONEFLAG_IS_AFTER_PHYSICS_DEFORM + cdef public int BONEFLAG_IS_EXTERNAL_PARENT_DEFORM + + +cdef class RigidBody: + cdef public str name + cdef public str english_name + cdef public int bone_index + cdef public int collision_group + cdef public int no_collision_group + cdef public int shape_type + cdef public MVector3D shape_size + cdef public MVector3D shape_position + cdef public MVector3D shape_rotation + cdef public object param + cdef public int mode + cdef public int index + cdef public str bone_name + cdef public bint is_arm_upper + cdef public bint is_small + cdef public int SHAPE_SPHERE + cdef public int SHAPE_BOX + cdef public int SHAPE_CAPSULE + + +cdef class RigidBodyParam: + cdef public float mass + cdef public float linear_damping + cdef public float angular_damping + cdef public float restitution + cdef public float friction + + +cdef class OBB: + cdef public int fno + cdef public MVector3D shape_size + cdef public MVector3D shape_position + cdef public MVector3D shape_rotation + cdef public MQuaternion shape_rotation_qq + cdef public MVector3D bone_pos + cdef public int h_sign + cdef public int v_sign + cdef public bint is_aliginment + cdef public bint is_arm_upper + cdef public bint is_small + cdef public bint is_arm_left + cdef public MMatrix4x4 matrix + cdef public MMatrix4x4 rotated_matrix + cdef public MVector3D origin + cdef public dict origin_xyz + cdef public dict shape_size_xyz + + cpdef tuple get_collistion(self, MVector3D point, MVector3D root_global_pos, float max_length) + +cdef class PmxModel: + cdef public str path + cdef public str name + cdef public str english_name + cdef public str comment + cdef public str english_comment + cdef public dict vertices + cdef public list indices + cdef public list textures + cdef public dict materials + cdef public dict material_indexes + cdef public dict bones + cdef public dict bone_indexes + cdef public dict morphs + cdef public dict display_slots + cdef public dict rigidbodies + cdef public dict rigidbody_indexes + cdef public dict joints + cdef public str digest + cdef public bint can_upper_sizing + cdef public bint can_arm_sizing + cdef public Vertex head_top_vertex + cdef public Vertex left_sole_vertex + cdef public Vertex right_sole_vertex + cdef public Vertex left_toe_vertex + cdef public Vertex right_toe_vertex + cdef public Vertex left_ik_sole_vertex + cdef public Vertex right_ik_sole_vertex + cdef public Vertex finger_tail_vertex + cdef public dict wrist_entity_vertex + cdef public dict elbow_entity_vertex + cdef public dict elbow_middle_entity_vertex diff --git a/src/mmd/PmxData.py b/src/mmd/PmxData.pyx similarity index 67% rename from src/mmd/PmxData.py rename to src/mmd/PmxData.pyx index 2c5a979..52d8f7d 100644 --- a/src/mmd/PmxData.py +++ b/src/mmd/PmxData.pyx @@ -14,8 +14,83 @@ logger = MLogger(__name__, level=MLogger.DEBUG) +cdef class Deform: + def __init__(self, index0): + self.index0 = index0 + +class Bdef1(Deform): + def __init__(self, index0): + self.index0 = index0 + + def get_idx_list(self): + return [self.index0] + + def __str__(self): + return "".format(self.index0) + +class Bdef2(Deform): + def __init__(self, index0, index1, weight0): + self.index0 = index0 + self.index1 = index1 + self.weight0 = weight0 + + def get_idx_list(self): + return [self.index0, self.index1] + + def __str__(self): + return "".format(self.index0, self.index1, self.weight0) + +class Bdef4(Deform): + def __init__(self, index0, index1, index2, index3, weight0, weight1, weight2, weight3): + self.index0 = index0 + self.index1 = index1 + self.index2 = index2 + self.index3 = index3 + self.weight0 = weight0 + self.weight1 = weight1 + self.weight2 = weight2 + self.weight3 = weight3 + + def get_idx_list(self): + return [self.index0, self.index1, self.index2, self.index3] + + def __str__(self): + return "".format(self.index0, self.index1, self.index2, self.index3, self.weight0, self.weight1, self.weight2, self.weight3) + +class Sdef(Deform): + def __init__(self, index0, index1, weight0, sdef_c, sdef_r0, sdef_r1): + self.index0 = index0 + self.index1 = index1 + self.weight0 = weight0 + self.sdef_c = sdef_c + self.sdef_r0 = sdef_r0 + self.sdef_r1 = sdef_r1 + + def get_idx_list(self): + return [self.index0, self.index1] + + def __str__(self): + return "".format(self.index0, self.index1, self.weight0, self.sdef_c, self.sdef_r0, self.sdef_r1) + +class Qdef(Deform): + def __init__(self, index0, index1, weight0, sdef_c, sdef_r0, sdef_r1): + self.index0 = index0 + self.index1 = index1 + self.weight0 = weight0 + self.sdef_c = sdef_c + self.sdef_r0 = sdef_r0 + self.sdef_r1 = sdef_r1 + + def get_idx_list(self): + return [self.index0, self.index1] + + def __str__(self): + return "".format(self.index0, self.index1, self.weight0, self.sdef_c, self.sdef_r0, self.sdef_r1) + + # 頂点構造 ---------------------------- -class Vertex(): +cdef class Vertex: + def __init__(self, index, position, normal, uv, extended_uvs, deform, edge_factor): self.index = index self.position = position @@ -30,23 +105,23 @@ def __str__(self): self.index, self.position, self.normal, self.uv, len(self.extended_uvs), self.deform, self.edge_factor) def is_deform_index(self, target_idx): - if type(self.deform) is PmxModel.Bdef1: + if type(self.deform) is Bdef1: return self.deform.index0 == target_idx - elif type(self.deform) is PmxModel.Bdef2: + elif type(self.deform) is Bdef2: return self.deform.index0 == target_idx or self.deform.index1 == target_idx - elif type(self.deform) is PmxModel.Bdef4: + elif type(self.deform) is Bdef4: return self.deform.index0 == target_idx or self.deform.index1 == target_idx \ or self.deform.index2 == target_idx or self.deform.index3 == target_idx - elif type(self.deform) is PmxModel.Sdef: + elif type(self.deform) is Sdef: return self.deform.index0 == target_idx or self.deform.index1 == target_idx - elif type(self.deform) is PmxModel.Qdef: + elif type(self.deform) is Qdef: return self.deform.index0 == target_idx or self.deform.index1 == target_idx return False # 最もウェイトが乗っているボーンINDEX def get_max_deform_index(self, head_links_indexes): - if type(self.deform) is Vertex.Bdef2 or type(self.deform) is Vertex.Sdef or type(self.deform) is Vertex.Qdef: + if type(self.deform) is Bdef2 or type(self.deform) is Sdef or type(self.deform) is Qdef: if self.deform.weight0 >= 0.5 and self.deform.index0 in head_links_indexes.keys(): return self.deform.index0 else: @@ -55,7 +130,7 @@ def get_max_deform_index(self, head_links_indexes): else: return self.deform.index0 - elif type(self.deform) is Vertex.Bdef4: + elif type(self.deform) is Bdef4: # 上半身系INDEXにウェイトが乗っているボーンのみ対象 target_weights = [] @@ -80,79 +155,11 @@ def get_max_deform_index(self, head_links_indexes): return self.deform.index0 return self.deform.index0 - - class Bdef1(): - def __init__(self, index0): - self.index0 = index0 - - def get_idx_list(self): - return [self.index0] - - def __str__(self): - return "".format(self.index0) - - class Bdef2(): - def __init__(self, index0, index1, weight0): - self.index0 = index0 - self.index1 = index1 - self.weight0 = weight0 - - def get_idx_list(self): - return [self.index0, self.index1] - - def __str__(self): - return "".format(self.index0, self.index1, self.weight0) - - class Bdef4(): - def __init__(self, index0, index1, index2, index3, weight0, weight1, weight2, weight3): - self.index0 = index0 - self.index1 = index1 - self.index2 = index2 - self.index3 = index3 - self.weight0 = weight0 - self.weight1 = weight1 - self.weight2 = weight2 - self.weight3 = weight3 - - def get_idx_list(self): - return [self.index0, self.index1, self.index2, self.index3] - - def __str__(self): - return "".format(self.index0, self.index1, self.index2, self.index3, self.weight0, self.weight1, self.weight2, self.weight3) - - class Sdef(): - def __init__(self, index0, index1, weight0, sdef_c, sdef_r0, sdef_r1): - self.index0 = index0 - self.index1 = index1 - self.weight0 = weight0 - self.sdef_c = sdef_c - self.sdef_r0 = sdef_r0 - self.sdef_r1 = sdef_r1 - - def get_idx_list(self): - return [self.index0, self.index1] - - def __str__(self): - return "".format(self.index0, self.index1, self.weight0, self.sdef_c, self.sdef_r0, self.sdef_r1) - - class Qdef(): - def __init__(self, index0, index1, weight0, sdef_c, sdef_r0, sdef_r1): - self.index0 = index0 - self.index1 = index1 - self.weight0 = weight0 - self.sdef_c = sdef_c - self.sdef_r0 = sdef_r0 - self.sdef_r1 = sdef_r1 - - def get_idx_list(self): - return [self.index0, self.index1] - - def __str__(self): - return "".format(self.index0, self.index1, self.weight0, self.sdef_c, self.sdef_r0, self.sdef_r1) + # 材質構造----------------------- -class Material(): +class Material: def __init__(self, name, english_name, diffuse_color, alpha, specular_factor, specular_color, ambient_color, flag, edge_color, edge_size, texture_index, sphere_texture_index, sphere_mode, toon_sharing_flag, toon_texture_index=0, comment="", vertex_count=0): self.name = name @@ -183,9 +190,29 @@ def __str__(self): self.sphere_texture_index, self.sphere_mode, self.toon_sharing_flag, self.toon_texture_index, self.comment, self.vertex_count) +cdef class Ik: + def __init__(self, target_index, loop, limit_radian, link=None): + self.target_index = target_index + self.loop = loop + self.limit_radian = limit_radian + self.link = link or [] + + def __str__(self): + return "= max_length: + # 最大可能距離より長い場合、縮める + x_arm_local *= (max_length / x_arm_local.length()) * 0.98 + rep_x_collision_vec = arm_matrix * x_arm_local + new_x_local = self.matrix.inverted() * rep_x_collision_vec x_distance = new_x_local.distanceToPoint(local_point) + + if z_arm_local.length() >= max_length: + # 最大可能距離より長い場合、縮める + z_arm_local *= (max_length / z_arm_local.length()) * 0.98 + rep_z_collision_vec = arm_matrix * z_arm_local + new_z_local = self.matrix.inverted() * rep_z_collision_vec z_distance = new_z_local.distanceToPoint(local_point) - rep_x_collision_vec = self.matrix * new_x_local - rep_z_collision_vec = self.matrix * new_z_local - - # 腕の位置を起点とする行列(移動量だけ見る) - arm_matrix = MMatrix4x4() - arm_matrix.setToIdentity() - arm_matrix.translate(root_global_pos) - - # 腕から見た回避位置 - x_arm_local = arm_matrix.inverted() * rep_x_collision_vec - z_arm_local = arm_matrix.inverted() * rep_z_collision_vec - - if x_arm_local.length() >= max_length: - # 最大可能距離より長い場合、縮める - x_arm_local *= (max_length / x_arm_local.length()) * 0.98 - rep_x_collision_vec = arm_matrix * x_arm_local - new_x_local = self.matrix.inverted() * rep_x_collision_vec - x_distance = new_x_local.distanceToPoint(local_point) - - if z_arm_local.length() >= max_length: - # 最大可能距離より長い場合、縮める - z_arm_local *= (max_length / z_arm_local.length()) * 0.98 - rep_z_collision_vec = arm_matrix * z_arm_local - new_z_local = self.matrix.inverted() * rep_z_collision_vec - z_distance = new_z_local.distanceToPoint(local_point) - - logger.debug("f: %s, y: %s, yt: %s, sy: %s, xt: %s, sx: %s, zt: %s, sz: %s, xd: %s, zd: %s, l: %s, d: %s, xl: %s, zl: %s, xr: %s, zr: %s", \ - self.fno, local_point.y() / y, y_theta, sin_y_theta, x_theta, sin_x_theta, z_theta, sin_z_theta, x_distance, z_distance, local_point.to_log(), d, \ - new_x_local.to_log(), new_z_local.to_log(), rep_x_collision_vec, rep_z_collision_vec) - - # 3方向の間に点が含まれていたら衝突あり - return collision, near_collision, x_distance, z_distance, rep_x_collision_vec, rep_z_collision_vec - - # 箱剛体 - class Box(OBB): - def __init__(self, *args): - super().__init__(*args) - - # 衝突しているか(内外判定) - # https://stackoverflow.com/questions/21037241/how-to-determine-a-point-is-inside-or-outside-a-cube - def get_collistion(self, point: MVector3D, root_global_pos: MVector3D, max_length: float): - # 立方体の中にある場合、衝突 - - # --------- - # 下辺 - b1 = self.matrix * MVector3D(-self.shape_size.x(), -self.shape_size.y(), -self.shape_size.z()) - b2 = self.matrix * MVector3D(self.shape_size.x(), -self.shape_size.y(), -self.shape_size.z()) - b4 = self.matrix * MVector3D(-self.shape_size.x(), -self.shape_size.y(), self.shape_size.z()) - # 上辺 - t1 = self.matrix * MVector3D(-self.shape_size.x(), self.shape_size.y(), -self.shape_size.z()) - - d1 = (t1 - b1) - size1 = d1.length() - dir1 = d1 / size1 - dir1.effective() - - d2 = (b2 - b1) - size2 = d2.length() - dir2 = d2 / size2 - dir2.effective() - - d3 = (b4 - b1) - size3 = d3.length() - dir3 = d3 / size3 - dir3.effective() - - dir_vec = point - self.origin - dir_vec.effective() - - res1 = abs(MVector3D.dotProduct(dir_vec, dir1)) * 2 < size1 - res2 = abs(MVector3D.dotProduct(dir_vec, dir2)) * 2 < size2 - res3 = abs(MVector3D.dotProduct(dir_vec, dir3)) * 2 < size3 - - # 3方向の間に点が含まれていたら衝突あり - collision = (res1 and res2 and res3 and True) - - # --------- - # 下辺 - b1 = self.matrix * MVector3D(-self.shape_size.x(), -self.shape_size.y(), -self.shape_size.z()) * 1.02 - b2 = self.matrix * MVector3D(self.shape_size.x(), -self.shape_size.y(), -self.shape_size.z()) * 1.02 - b4 = self.matrix * MVector3D(-self.shape_size.x(), -self.shape_size.y(), self.shape_size.z()) * 1.02 - # 上辺 - t1 = self.matrix * MVector3D(-self.shape_size.x(), self.shape_size.y(), -self.shape_size.z()) * 1.02 - - d1 = (t1 - b1) - size1 = d1.length() - dir1 = d1 / size1 - dir1.effective() - - d2 = (b2 - b1) - size2 = d2.length() - dir2 = d2 / size2 - dir2.effective() - - d3 = (b4 - b1) - size3 = d3.length() - dir3 = d3 / size3 - dir3.effective() - - dir_vec = point - self.origin - dir_vec.effective() - - res1 = abs(MVector3D.dotProduct(dir_vec, dir1)) * 2 < size1 - res2 = abs(MVector3D.dotProduct(dir_vec, dir2)) * 2 < size2 - res3 = abs(MVector3D.dotProduct(dir_vec, dir3)) * 2 < size3 - - # 3方向の間に点が含まれていたら衝突あり - near_collision = (res1 and res2 and res3 and True) - - x_distance = 0 - z_distance = 0 - rep_x_collision_vec = MVector3D() - rep_z_collision_vec = MVector3D() - - if collision or near_collision: - # 左右の腕のどちらと衝突しているかにより、元に戻す方向が逆になる - x = self.shape_size.x() * 1.02 * self.h_sign - z = -self.shape_size.z() * 1.02 - - # X方向にOBBの境界に持って行った場合の位置 - x_base = self.rotated_matrix * MVector3D(x, 0, 0) - # Z方向に同上 - z_base = self.rotated_matrix * MVector3D(0, 0, z) - logger.test("x_base: %s", x_base) - logger.test("z_base: %s", z_base) + logger.debug("f: %s, y: %s, yt: %s, sy: %s, xt: %s, sx: %s, zt: %s, sz: %s, xd: %s, zd: %s, l: %s, d: %s, xl: %s, zl: %s, xr: %s, zr: %s", \ + self.fno, local_point.y() / y, y_theta, sin_y_theta, x_theta, sin_x_theta, z_theta, sin_z_theta, x_distance, z_distance, local_point.to_log(), d, \ + new_x_local.to_log(), new_z_local.to_log(), rep_x_collision_vec, rep_z_collision_vec) + + # 3方向の間に点が含まれていたら衝突あり + return (collision, near_collision, x_distance, z_distance, rep_x_collision_vec, rep_z_collision_vec) + +# 箱剛体 +cdef class Box(OBB): + def __init__(self, *args): + super().__init__(*args) + + # 衝突しているか(内外判定) + # https://stackoverflow.com/questions/21037241/how-to-determine-a-point-is-inside-or-outside-a-cube + cpdef tuple get_collistion(self, MVector3D point, MVector3D root_global_pos, float max_length): + cdef MMatrix4x4 arm_matrix + cdef bint collision, near_collision, res1, res2, res3 + cdef double d, sin_x_theta, sin_y_theta, sin_z_theta, x_theta, y, y_theta, z_theta, new_y, size1, size2, size3, x, z, x_diff, z_diff, x_distance, z_distance + cdef MVector3D b1, b2, b4, d1, d2, d3, dir1, dir2, dir3, dir_vec, local_point, new_x_local, new_z_local, rep_x_collision_vec, rep_z_collision_vec, t1 + cdef MVector3D x_arm_local, x_base, z_arm_local, z_base + + # 立方体の中にある場合、衝突 + + # --------- + # 下辺 + b1 = self.matrix * MVector3D(-self.shape_size.x(), -self.shape_size.y(), -self.shape_size.z()) + b2 = self.matrix * MVector3D(self.shape_size.x(), -self.shape_size.y(), -self.shape_size.z()) + b4 = self.matrix * MVector3D(-self.shape_size.x(), -self.shape_size.y(), self.shape_size.z()) + # 上辺 + t1 = self.matrix * MVector3D(-self.shape_size.x(), self.shape_size.y(), -self.shape_size.z()) + + d1 = (t1 - b1) + size1 = d1.length() + dir1 = d1 / size1 + dir1.effective() + + d2 = (b2 - b1) + size2 = d2.length() + dir2 = d2 / size2 + dir2.effective() + + d3 = (b4 - b1) + size3 = d3.length() + dir3 = d3 / size3 + dir3.effective() + + dir_vec = point - self.origin + dir_vec.effective() + + res1 = abs(MVector3D.dotProduct(dir_vec, dir1)) * 2 < size1 + res2 = abs(MVector3D.dotProduct(dir_vec, dir2)) * 2 < size2 + res3 = abs(MVector3D.dotProduct(dir_vec, dir3)) * 2 < size3 + + # 3方向の間に点が含まれていたら衝突あり + collision = (res1 and res2 and res3 and True) + + # --------- + # 下辺 + b1 = self.matrix * MVector3D(-self.shape_size.x(), -self.shape_size.y(), -self.shape_size.z()) * 1.02 + b2 = self.matrix * MVector3D(self.shape_size.x(), -self.shape_size.y(), -self.shape_size.z()) * 1.02 + b4 = self.matrix * MVector3D(-self.shape_size.x(), -self.shape_size.y(), self.shape_size.z()) * 1.02 + # 上辺 + t1 = self.matrix * MVector3D(-self.shape_size.x(), self.shape_size.y(), -self.shape_size.z()) * 1.02 + + d1 = (t1 - b1) + size1 = d1.length() + dir1 = d1 / size1 + dir1.effective() + + d2 = (b2 - b1) + size2 = d2.length() + dir2 = d2 / size2 + dir2.effective() + + d3 = (b4 - b1) + size3 = d3.length() + dir3 = d3 / size3 + dir3.effective() + + dir_vec = point - self.origin + dir_vec.effective() + + res1 = abs(MVector3D.dotProduct(dir_vec, dir1)) * 2 < size1 + res2 = abs(MVector3D.dotProduct(dir_vec, dir2)) * 2 < size2 + res3 = abs(MVector3D.dotProduct(dir_vec, dir3)) * 2 < size3 + + # 3方向の間に点が含まれていたら衝突あり + near_collision = (res1 and res2 and res3 and True) + + x_distance = 0 + z_distance = 0 + rep_x_collision_vec = MVector3D() + rep_z_collision_vec = MVector3D() + + if collision or near_collision: + # 左右の腕のどちらと衝突しているかにより、元に戻す方向が逆になる + x = self.shape_size.x() * 1.02 * self.h_sign + z = -self.shape_size.z() * 1.02 + + # X方向にOBBの境界に持って行った場合の位置 + x_base = self.rotated_matrix * MVector3D(x, 0, 0) + # Z方向に同上 + z_base = self.rotated_matrix * MVector3D(0, 0, z) + logger.test("x_base: %s", x_base) + logger.test("z_base: %s", z_base) + + x_diff = x_base.distanceToPoint(point) + z_diff = z_base.distanceToPoint(point) + + logger.test("x_diff: %s", x_diff) + logger.test("z_diff: %s", z_diff) - x_diff = x_base.distanceToPoint(point) - z_diff = z_base.distanceToPoint(point) + # 剛体のローカル座標系に基づく点の位置 + local_point = self.rotated_matrix.inverted() * point - logger.test("x_diff: %s", x_diff) - logger.test("z_diff: %s", z_diff) + new_y = local_point.y() - # 剛体のローカル座標系に基づく点の位置 - local_point = self.rotated_matrix.inverted() * point + new_x_local = MVector3D(x, new_y, local_point.z()) + new_z_local = MVector3D(local_point.x(), new_y, z) - new_y = local_point.y() + x_distance = new_x_local.distanceToPoint(local_point) + z_distance = new_z_local.distanceToPoint(local_point) - new_x_local = MVector3D(x, new_y, local_point.z()) - new_z_local = MVector3D(local_point.x(), new_y, z) + rep_x_collision_vec = self.rotated_matrix * new_x_local + rep_z_collision_vec = self.rotated_matrix * new_z_local + # 腕の位置を起点とする行列(移動量だけ見る) + arm_matrix = MMatrix4x4() + arm_matrix.setToIdentity() + arm_matrix.translate(root_global_pos) + + # 腕から見た回避位置 + x_arm_local = arm_matrix.inverted() * rep_x_collision_vec + z_arm_local = arm_matrix.inverted() * rep_z_collision_vec + + if x_arm_local.length() >= max_length: + # 最大可能距離より長い場合、縮める + x_arm_local *= (max_length / x_arm_local.length()) * 0.98 + rep_x_collision_vec = arm_matrix * x_arm_local + new_x_local = self.matrix.inverted() * rep_x_collision_vec x_distance = new_x_local.distanceToPoint(local_point) - z_distance = new_z_local.distanceToPoint(local_point) - rep_x_collision_vec = self.rotated_matrix * new_x_local - rep_z_collision_vec = self.rotated_matrix * new_z_local - - # 腕の位置を起点とする行列(移動量だけ見る) - arm_matrix = MMatrix4x4() - arm_matrix.setToIdentity() - arm_matrix.translate(root_global_pos) - - # 腕から見た回避位置 - x_arm_local = arm_matrix.inverted() * rep_x_collision_vec - z_arm_local = arm_matrix.inverted() * rep_z_collision_vec - - if x_arm_local.length() >= max_length: - # 最大可能距離より長い場合、縮める - x_arm_local *= (max_length / x_arm_local.length()) * 0.98 - rep_x_collision_vec = arm_matrix * x_arm_local - new_x_local = self.matrix.inverted() * rep_x_collision_vec - x_distance = new_x_local.distanceToPoint(local_point) - - if z_arm_local.length() >= max_length: - # 最大可能距離より長い場合、縮める - z_arm_local *= (max_length / z_arm_local.length()) * 0.98 - rep_z_collision_vec = arm_matrix * z_arm_local - new_z_local = self.matrix.inverted() * rep_z_collision_vec - z_distance = new_z_local.distanceToPoint(local_point) - - logger.debug("f: %s, xd: %s, zd: %s, l: %s, xl: %s, zl: %s, xr: %s, zr: %s", \ - self.fno, x_distance, z_distance, local_point.to_log(), new_x_local.to_log(), new_z_local.to_log(), rep_x_collision_vec, rep_z_collision_vec) - - return collision, near_collision, x_distance, z_distance, rep_x_collision_vec, rep_z_collision_vec - - # カプセル剛体 - class Capsule(OBB): - def __init__(self, *args): - super().__init__(*args) - - # 衝突しているか - # http://marupeke296.com/COL_3D_No27_CapsuleCapsule.html - def get_collistion(self, point: MVector3D, root_global_pos: MVector3D, max_length: float): - # 下辺 - b1 = self.rotated_matrix * MVector3D(0, -self.shape_size.y(), 0) - # 上辺 - t1 = self.rotated_matrix * MVector3D(0, self.shape_size.y(), 0) - - # 垂線までの長さ - v = (t1 - b1) - lensq = v.lengthSquared() - t = 0 if lensq == 0 else MVector3D.dotProduct(v, point - b1) / lensq - # 垂線を下ろした座標 - h = b1 + (v * t) - - logger.test("v: %s", v) - logger.test("lensq: %s", lensq) - logger.test("t: %s", t) - logger.test("h: %s", h) - - # 点・下辺始点・垂線点の三角形 - ba = (point - b1).lengthSquared() - bb = (h - b1).lengthSquared() - bc = (point - h).lengthSquared() - - # 点・上辺終点・垂線点の三角形 - ta = (point - t1).lengthSquared() - tb = (h - t1).lengthSquared() - tc = (point - h).lengthSquared() - - logger.test("ba: %s, bb: %s, bc: %s", ba, bb, bc) - logger.test("ta: %s, tb: %s, tc: %s", ta, tb, tc) - - if t1.distanceToPoint(b1) < b1.distanceToPoint(h) < t1.distanceToPoint(h): - # b1側の外分点 - h = b1 - elif t1.distanceToPoint(b1) < t1.distanceToPoint(h) < b1.distanceToPoint(h): - # t1側の外分点 - h = t1 - - logger.test("v: %s", v) - logger.test("lensq: %s", lensq) - logger.test("t: %s", t) - logger.test("h: %s", h) - logger.test("point: %s", point) - logger.test("segl: %s", point.distanceToPoint(h)) - - # カプセルの線分から半径以内なら中に入っている - d = point.distanceToPoint(h) - collision = 0 < d < self.shape_size.x() * 0.98 - near_collision = 0 <= d <= self.shape_size.x() * 1.02 - - x_distance = 0 - z_distance = 0 - rep_x_collision_vec = MVector3D() - rep_z_collision_vec = MVector3D() - - if collision or near_collision: - # hのローカル座標系に基づく点の位置 - h_matrix = self.matrix.copy() - h_matrix.translate(self.matrix.inverted() * h) - local_point = h_matrix.inverted() * point - logger.debug("h: %s, localh: %s", h, h_matrix * MVector3D()) - - # 距離分だけ離した場合の球 - x = d * 1.02 * self.h_sign - y = d * 1.02 * self.v_sign - z = d * 1.02 * -1 # (np.sign(local_point.z()) if self.is_arm_upper else -1) - - # 各軸方向の離れ具合 - x_theta = math.acos(max(-1, min(1, local_point.x() / x))) - y_theta = math.acos(max(-1, min(1, abs(local_point.y()) / y))) - z_theta = math.acos(max(-1, min(1, local_point.z() / z))) - # 離れ具合から見た円周の位置 - sin_y_theta = math.sin(y_theta) * 1.02 - sin_x_theta = math.sin(x_theta) * 1.02 - sin_z_theta = math.sin(z_theta) * 1.02 - - new_y = local_point.y() - - new_x_local = MVector3D(y_theta * x, new_y, local_point.z()) - new_z_local = MVector3D(local_point.x(), new_y, y_theta * z) + if z_arm_local.length() >= max_length: + # 最大可能距離より長い場合、縮める + z_arm_local *= (max_length / z_arm_local.length()) * 0.98 + rep_z_collision_vec = arm_matrix * z_arm_local + new_z_local = self.matrix.inverted() * rep_z_collision_vec + z_distance = new_z_local.distanceToPoint(local_point) + logger.debug("f: %s, xd: %s, zd: %s, l: %s, xl: %s, zl: %s, xr: %s, zr: %s", \ + self.fno, x_distance, z_distance, local_point.to_log(), new_x_local.to_log(), new_z_local.to_log(), rep_x_collision_vec, rep_z_collision_vec) + + return (collision, near_collision, x_distance, z_distance, rep_x_collision_vec, rep_z_collision_vec) + +# カプセル剛体 +cdef class Capsule(OBB): + def __init__(self, *args): + super().__init__(*args) + + # 衝突しているか + # http://marupeke296.com/COL_3D_No27_CapsuleCapsule.html + cpdef tuple get_collistion(self, MVector3D point, MVector3D root_global_pos, float max_length): + cdef MMatrix4x4 arm_matrix + cdef bint collision, near_collision + cdef double d, sin_x_theta, sin_y_theta, sin_z_theta, x, x_theta, y, y_theta, z, z_theta, x_distance, z_distance, new_y + cdef MVector3D local_point, new_x_local, new_z_local, rep_x_collision_vec, rep_z_collision_vec, x_arm_local, z_arm_local + cdef MVector3D b1, t1, h, v + cdef double ba, bb, bc, ta, tb, tc, lensq, t + + # 下辺 + b1 = self.rotated_matrix * MVector3D(0, -self.shape_size.y(), 0) + # 上辺 + t1 = self.rotated_matrix * MVector3D(0, self.shape_size.y(), 0) + + # 垂線までの長さ + v = (t1 - b1) + lensq = v.lengthSquared() + t = 0 if lensq == 0 else MVector3D.dotProduct(v, point - b1) / lensq + # 垂線を下ろした座標 + h = b1 + (v * t) + + logger.test("v: %s", v) + logger.test("lensq: %s", lensq) + logger.test("t: %s", t) + logger.test("h: %s", h) + + # 点・下辺始点・垂線点の三角形 + ba = (point - b1).lengthSquared() + bb = (h - b1).lengthSquared() + bc = (point - h).lengthSquared() + + # 点・上辺終点・垂線点の三角形 + ta = (point - t1).lengthSquared() + tb = (h - t1).lengthSquared() + tc = (point - h).lengthSquared() + + logger.test("ba: %s, bb: %s, bc: %s", ba, bb, bc) + logger.test("ta: %s, tb: %s, tc: %s", ta, tb, tc) + + if t1.distanceToPoint(b1) < b1.distanceToPoint(h) < t1.distanceToPoint(h): + # b1側の外分点 + h = b1 + elif t1.distanceToPoint(b1) < t1.distanceToPoint(h) < b1.distanceToPoint(h): + # t1側の外分点 + h = t1 + + logger.test("v: %s", v) + logger.test("lensq: %s", lensq) + logger.test("t: %s", t) + logger.test("h: %s", h) + logger.test("point: %s", point) + logger.test("segl: %s", point.distanceToPoint(h)) + + # カプセルの線分から半径以内なら中に入っている + d = point.distanceToPoint(h) + collision = 0 < d < self.shape_size.x() * 0.98 + near_collision = 0 <= d <= self.shape_size.x() * 1.02 + + x_distance = 0 + z_distance = 0 + rep_x_collision_vec = MVector3D() + rep_z_collision_vec = MVector3D() + + if collision or near_collision: + # hのローカル座標系に基づく点の位置 + h_matrix = self.matrix.copy() + h_matrix.translate(self.matrix.inverted() * h) + local_point = h_matrix.inverted() * point + logger.debug("h: %s, localh: %s", h, h_matrix * MVector3D()) + + # 距離分だけ離した場合の球 + x = d * 1.02 * self.h_sign + y = d * 1.02 * self.v_sign + z = d * 1.02 * -1 # (np.sign(local_point.z()) if self.is_arm_upper else -1) + + # 各軸方向の離れ具合 + x_theta = math.acos(max(-1, min(1, local_point.x() / x))) + y_theta = math.acos(max(-1, min(1, abs(local_point.y()) / y))) + z_theta = math.acos(max(-1, min(1, local_point.z() / z))) + # 離れ具合から見た円周の位置 + sin_y_theta = math.sin(y_theta) * 1.02 + sin_x_theta = math.sin(x_theta) * 1.02 + sin_z_theta = math.sin(z_theta) * 1.02 + + new_y = local_point.y() + + new_x_local = MVector3D(y_theta * x, new_y, local_point.z()) + new_z_local = MVector3D(local_point.x(), new_y, y_theta * z) + + x_distance = new_x_local.distanceToPoint(local_point) + z_distance = new_z_local.distanceToPoint(local_point) + + rep_x_collision_vec = h_matrix * new_x_local + rep_z_collision_vec = h_matrix * new_z_local + + # 腕の位置を起点とする行列(移動量だけ見る) + arm_matrix = MMatrix4x4() + arm_matrix.setToIdentity() + arm_matrix.translate(root_global_pos) + + # 腕から見た回避位置 + x_arm_local = arm_matrix.inverted() * rep_x_collision_vec + z_arm_local = arm_matrix.inverted() * rep_z_collision_vec + + if x_arm_local.length() >= max_length: + # 最大可能距離より長い場合、縮める + x_arm_local *= (max_length / x_arm_local.length()) * 0.98 + rep_x_collision_vec = arm_matrix * x_arm_local + new_x_local = h_matrix.inverted() * rep_x_collision_vec x_distance = new_x_local.distanceToPoint(local_point) + + if z_arm_local.length() >= max_length: + # 最大可能距離より長い場合、縮める + z_arm_local *= (max_length / z_arm_local.length()) * 0.98 + rep_z_collision_vec = arm_matrix * z_arm_local + new_z_local = h_matrix.inverted() * rep_z_collision_vec z_distance = new_z_local.distanceToPoint(local_point) - rep_x_collision_vec = h_matrix * new_x_local - rep_z_collision_vec = h_matrix * new_z_local - - # 腕の位置を起点とする行列(移動量だけ見る) - arm_matrix = MMatrix4x4() - arm_matrix.setToIdentity() - arm_matrix.translate(root_global_pos) - - # 腕から見た回避位置 - x_arm_local = arm_matrix.inverted() * rep_x_collision_vec - z_arm_local = arm_matrix.inverted() * rep_z_collision_vec - - if x_arm_local.length() >= max_length: - # 最大可能距離より長い場合、縮める - x_arm_local *= (max_length / x_arm_local.length()) * 0.98 - rep_x_collision_vec = arm_matrix * x_arm_local - new_x_local = h_matrix.inverted() * rep_x_collision_vec - x_distance = new_x_local.distanceToPoint(local_point) - - if z_arm_local.length() >= max_length: - # 最大可能距離より長い場合、縮める - z_arm_local *= (max_length / z_arm_local.length()) * 0.98 - rep_z_collision_vec = arm_matrix * z_arm_local - new_z_local = h_matrix.inverted() * rep_z_collision_vec - z_distance = new_z_local.distanceToPoint(local_point) - - logger.debug("f: %s, localy: %s, y_theta: %s, sin_y_theta: %s, x_theta: %s, sin_x_theta: %s, z_theta: %s, sin_z_theta: %s, x_distance: %s, z_distance: %s, "\ - "local_point: [%s], d: %s, new_x_local: %s, new_z_local: %s, rep_x_collision_vec: %s, rep_z_collision_vec: %s", \ - self.fno, local_point.y() / y, y_theta, sin_y_theta, x_theta, sin_x_theta, z_theta, sin_z_theta, x_distance, z_distance, local_point.to_log(), d, \ - new_x_local.to_log(), new_z_local.to_log(), rep_x_collision_vec, rep_z_collision_vec) - - # 3方向の間に点が含まれていたら衝突あり - return collision, near_collision, x_distance, z_distance, rep_x_collision_vec, rep_z_collision_vec - - class RigidBodyParam(): - def __init__(self, mass, linear_damping, angular_damping, restitution, friction): - self.mass = mass - self.linear_damping = linear_damping - self.angular_damping = angular_damping - self.restitution = restitution - self.friction = friction - - def __str__(self): - return "= 0 and bone.tail_index in self.bone_indexes and self.bones[self.bone_indexes[bone.tail_index]].position != bone.position: - # 表示先が指定されているの場合、保持 - to_pos = self.bones[self.bone_indexes[bone.tail_index]].position - else: - # 表示先がない場合、とりあえず子ボーンのどれかを選択 - for b in self.bones.values(): - if b.parent_index == bone.index and self.bones[self.bone_indexes[b.index]].position != bone.position: - to_pos = self.bones[self.bone_indexes[b.index]].position - break - - # 軸制限の指定が無い場合、子の方向 - x_axis = (to_pos - from_pos).normalized() + fixed_x_axis = MVector3D() + + from_pos = self.bones[bone.name].position + if bone.tail_position != MVector3D(): + # 表示先が相対パスの場合、保持 + to_pos = from_pos + bone.tail_position + elif bone.tail_index >= 0 and bone.tail_index in self.bone_indexes and self.bones[self.bone_indexes[bone.tail_index]].position != bone.position: + # 表示先が指定されているの場合、保持 + to_pos = self.bones[self.bone_indexes[bone.tail_index]].position + else: + # 表示先がない場合、とりあえず子ボーンのどれかを選択 + for b in self.bones.values(): + if b.parent_index == bone.index and self.bones[self.bone_indexes[b.index]].position != bone.position: + to_pos = self.bones[self.bone_indexes[b.index]].position + break + + # 軸制限の指定が無い場合、子の方向 + x_axis = (to_pos - from_pos).normalized() + + if fixed_x_axis != MVector3D() and np.sign(fixed_x_axis.x()) != np.sign(x_axis.x()): + # 軸制限の軸方向と計算上の軸方向が違う場合、逆ベクトル + x_axis = -fixed_x_axis return x_axis @@ -989,6 +1016,7 @@ def calc_stance(self, from_bone_name: str, to_bone_name: str, default_pos: MVect to_pos = from_pos + default_pos from_qq = MQuaternion() + diff_pos = MVector3D() if from_pos != MVector3D() and to_pos != MVector3D(): logger.test("from_pos: %s", from_pos) logger.test("to_pos: %s", to_pos) @@ -1100,7 +1128,25 @@ def create_link_2_top(self, target_bone_name: str, links: BoneLinks, is_defined: raise SizingException("ボーンリンクの生成に失敗しました。\nモデル「{0}」の「{1}」ボーンで以下を確認してください。\n" \ + "・同じ名前のボーンが複数ないか(ボーンのINDEXがズレるため、サイジングに失敗します)\n" \ + "・親ボーンに自分の名前と同じ名前のボーンが指定されていないか\n※ PMXEditorの「PMXデータの状態検証」から確認できます。".format(self.name, target_bone_name)) + + # 子孫ボーンリスト取得 + def get_child_bones(self, target_bone: Bone, bone_list=None): + if not bone_list: + bone_list = [] + child_bone_list = [] + + for child_bone in self.bones.values(): + if child_bone.index != target_bone.index and child_bone.parent_index == target_bone.index: + # 処理対象ボーンが親INDEXに入ってる場合、処理対象 + bone_list.append(child_bone) + child_bone_list.append(child_bone) + + for child_bone in child_bone_list: + self.get_child_bones(child_bone, bone_list) + + return bone_list + # ボーン関係親子のペア PARENT_BORN_PAIR = { "SIZING_ROOT_BONE": [""], @@ -1240,11 +1286,11 @@ def get_head_top_vertex(self): if not up_max_vertex: if "頭" in self.bones: - return Vertex(-1, self.bones["頭"].position.copy(), MVector3D(), [], [], Vertex.Bdef1(-1), -1) + return Vertex(-1, self.bones["頭"].position.copy(), MVector3D(), [], [], Bdef1(-1), -1) elif "首" in self.bones: - return Vertex(-1, self.bones["首"].position.copy(), MVector3D(), [], [], Vertex.Bdef1(-1), -1) + return Vertex(-1, self.bones["首"].position.copy(), MVector3D(), [], [], Bdef1(-1), -1) else: - return Vertex(-1, MVector3D(), MVector3D(), [], [], Vertex.Bdef1(-1), -1) + return Vertex(-1, MVector3D(), MVector3D(), [], [], Bdef1(-1), -1) return up_max_vertex @@ -1284,8 +1330,8 @@ def get_head_rigidbody(self): # Zはちょっと前に center.setZ(center.z() - (radius * 0.05)) - head_rigidbody = RigidBody("頭接触回避", None, self.bones["頭"].index, None, None, 0, \ - MVector3D(radius, radius, radius), center, MVector3D(), None, None, None, None, None, 0) + head_rigidbody = RigidBody("頭接触回避", None, self.bones["頭"].index, 0, 0, 0, \ + MVector3D(radius, radius, radius), center, MVector3D(), 0, 0, 0, 0, 0, 0) head_rigidbody.bone_name = "頭" head_rigidbody.is_arm_upper = True @@ -1305,7 +1351,7 @@ def get_toe_vertex(self, direction: str): target_bone_name = "{0}足IK".format(direction) else: # 足末端系ボーンがない場合、処理終了 - return Vertex(-1, MVector3D(), MVector3D(), [], [], Vertex.Bdef1(-1), -1) + return Vertex(-1, MVector3D(), MVector3D(), [], [], Bdef1(-1), -1) # 足末端系ボーン for bk, bv in self.bones.items(): @@ -1316,15 +1362,15 @@ def get_toe_vertex(self, direction: str): if len(bone_name_list) == 0: # ウェイトボーンがない場合、つま先ボーン系の位置 if "{0}つま先".format(direction) in self.bones: - return Vertex(-1, self.bones["{0}つま先".format(direction)].position, MVector3D(), [], [], Vertex.Bdef1(-1), -1) + return Vertex(-1, self.bones["{0}つま先".format(direction)].position, MVector3D(), [], [], Bdef1(-1), -1) elif "{0}つま先IK".format(direction) in self.bones: - return Vertex(-1, self.bones["{0}つま先IK".format(direction)].position, MVector3D(), [], [], Vertex.Bdef1(-1), -1) + return Vertex(-1, self.bones["{0}つま先IK".format(direction)].position, MVector3D(), [], [], Bdef1(-1), -1) elif "{0}足首".format(direction) in self.bones: - return Vertex(-1, self.bones["{0}足首".format(direction)].position, MVector3D(), [], [], Vertex.Bdef1(-1), -1) + return Vertex(-1, self.bones["{0}足首".format(direction)].position, MVector3D(), [], [], Bdef1(-1), -1) elif "{0}足IK".format(direction) in self.bones: - return Vertex(-1, self.bones["{0}足IK".format(direction)].position, MVector3D(), [], [], Vertex.Bdef1(-1), -1) + return Vertex(-1, self.bones["{0}足IK".format(direction)].position, MVector3D(), [], [], Bdef1(-1), -1) else: - return Vertex(-1, MVector3D(), MVector3D(), [], [], Vertex.Bdef1(-1), -1) + return Vertex(-1, MVector3D(), MVector3D(), [], [], Bdef1(-1), -1) up_max_pos, up_max_vertex, down_max_pos, down_max_vertex, right_max_pos, right_max_vertex, left_max_pos, left_max_vertex, \ back_max_pos, back_max_vertex, front_max_pos, front_max_vertex, multi_max_pos, multi_max_vertex \ @@ -1334,15 +1380,15 @@ def get_toe_vertex(self, direction: str): if not front_max_vertex: # つま先頂点が取れなかった場合 if "{0}つま先".format(direction) in self.bones: - return Vertex(-1, self.bones["{0}つま先".format(direction)].position, MVector3D(), [], [], Vertex.Bdef1(-1), -1) + return Vertex(-1, self.bones["{0}つま先".format(direction)].position, MVector3D(), [], [], Bdef1(-1), -1) elif "{0}つま先IK".format(direction) in self.bones: - return Vertex(-1, self.bones["{0}つま先IK".format(direction)].position, MVector3D(), [], [], Vertex.Bdef1(-1), -1) + return Vertex(-1, self.bones["{0}つま先IK".format(direction)].position, MVector3D(), [], [], Bdef1(-1), -1) elif "{0}足首".format(direction) in self.bones: - return Vertex(-1, self.bones["{0}足首".format(direction)].position, MVector3D(), [], [], Vertex.Bdef1(-1), -1) + return Vertex(-1, self.bones["{0}足首".format(direction)].position, MVector3D(), [], [], Bdef1(-1), -1) elif "{0}足IK".format(direction) in self.bones: - return Vertex(-1, self.bones["{0}足IK".format(direction)].position, MVector3D(), [], [], Vertex.Bdef1(-1), -1) + return Vertex(-1, self.bones["{0}足IK".format(direction)].position, MVector3D(), [], [], Bdef1(-1), -1) else: - return Vertex(-1, MVector3D(), MVector3D(), [], [], Vertex.Bdef1(-1), -1) + return Vertex(-1, MVector3D(), MVector3D(), [], [], Bdef1(-1), -1) return front_max_vertex @@ -1358,7 +1404,7 @@ def get_sole_vertex(self, direction: str): target_bone_name = "{0}足IK".format(direction) else: # 足末端系ボーンがない場合、処理終了 - return Vertex(-1, MVector3D(), MVector3D(), [], [], Vertex.Bdef1(-1), -1) + return Vertex(-1, MVector3D(), MVector3D(), [], [], Bdef1(-1), -1) # 足末端系ボーン for bk, bv in self.bones.items(): @@ -1369,9 +1415,9 @@ def get_sole_vertex(self, direction: str): if len(bone_name_list) == 0: # ウェイトボーンがない場合、足IKの位置 if "{0}足IK".format(direction) in self.bones: - return Vertex(-1, self.bones["{0}足IK".format(direction)].position, MVector3D(), [], [], Vertex.Bdef1(-1), -1) + return Vertex(-1, self.bones["{0}足IK".format(direction)].position, MVector3D(), [], [], Bdef1(-1), -1) else: - return Vertex(-1, MVector3D(), MVector3D(), [], [], Vertex.Bdef1(-1), -1) + return Vertex(-1, MVector3D(), MVector3D(), [], [], Bdef1(-1), -1) up_max_pos, up_max_vertex, down_max_pos, down_max_vertex, right_max_pos, right_max_vertex, left_max_pos, left_max_vertex, \ back_max_pos, back_max_vertex, front_max_pos, front_max_vertex, multi_max_pos, multi_max_vertex \ @@ -1381,9 +1427,9 @@ def get_sole_vertex(self, direction: str): if not multi_max_vertex: # 足底頂点が取れなかった場合 if "{0}足IK".format(direction) in self.bones: - return Vertex(-1, self.bones["{0}足IK".format(direction)].position, MVector3D(), [], [], Vertex.Bdef1(-1), -1) + return Vertex(-1, self.bones["{0}足IK".format(direction)].position, MVector3D(), [], [], Bdef1(-1), -1) else: - return Vertex(-1, MVector3D(), MVector3D(), [], [], Vertex.Bdef1(-1), -1) + return Vertex(-1, MVector3D(), MVector3D(), [], [], Bdef1(-1), -1) return multi_max_vertex @@ -1398,7 +1444,7 @@ def get_wrist_vertex(self, direction: str): bone_name_list.append(bk) else: # 手首ボーンがない場合、処理終了 - return Vertex(-1, MVector3D(), MVector3D(), [], [], Vertex.Bdef1(-1), -1) + return Vertex(-1, MVector3D(), MVector3D(), [], [], Bdef1(-1), -1) # 腕の傾き(正確にはひじ以降の傾き) _, arm_stance_qq = self.calc_arm_stance("{0}ひじ".format(direction), "{0}手首".format(direction)) @@ -1418,7 +1464,7 @@ def get_wrist_vertex(self, direction: str): if not down_max_vertex: # それでも取れなければ手首位置 - return Vertex(-1, self.bones["{0}手首".format(direction)].position.copy(), MVector3D(), [], [], Vertex.Bdef1(-1), -1) + return Vertex(-1, self.bones["{0}手首".format(direction)].position.copy(), MVector3D(), [], [], Bdef1(-1), -1) return down_max_vertex @@ -1432,7 +1478,7 @@ def get_finger_tail_vertex(self, finger_name: str, finger_tail_name: str): bone_name_list.append(finger_name) else: # 指ボーンがない場合、処理終了 - return Vertex(-1, MVector3D(), MVector3D(), [], [], Vertex.Bdef1(-1), -1) + return Vertex(-1, MVector3D(), MVector3D(), [], [], Bdef1(-1), -1) # 腕の傾き(正確にはひじ以降の傾き) _, arm_stance_qq = self.calc_arm_stance("{0}手首".format(direction), finger_name) @@ -1449,7 +1495,7 @@ def get_finger_tail_vertex(self, finger_name: str, finger_tail_name: str): return left_max_vertex # それでも取れなければ手首位置 - return Vertex(-1, self.bones[finger_name].position.copy(), MVector3D(), [], [], Vertex.Bdef1(-1), -1) + return Vertex(-1, self.bones[finger_name].position.copy(), MVector3D(), [], [], Bdef1(-1), -1) # ひじの厚みをはかる頂点を取得 def get_elbow_vertex(self, direction: str): @@ -1465,7 +1511,7 @@ def get_elbow_vertex(self, direction: str): bone_name_list.append(bk) else: # ひじボーンがない場合、処理終了 - return Vertex(-1, MVector3D(), MVector3D(), [], [], Vertex.Bdef1(-1), -1) + return Vertex(-1, MVector3D(), MVector3D(), [], [], Bdef1(-1), -1) # 腕の傾き(正確にはひじ以降の傾き) _, arm_stance_qq = self.calc_arm_stance("{0}腕".format(direction), "{0}ひじ".format(direction)) @@ -1485,7 +1531,7 @@ def get_elbow_vertex(self, direction: str): if not down_max_vertex: # それでも取れなければひじ位置 - return Vertex(-1, self.bones["{0}ひじ".format(direction)].position.copy(), MVector3D(), [], [], Vertex.Bdef1(-1), -1) + return Vertex(-1, self.bones["{0}ひじ".format(direction)].position.copy(), MVector3D(), [], [], Bdef1(-1), -1) return down_max_vertex diff --git a/src/mmd/PmxReader.py b/src/mmd/PmxReader.py index 9c84f2b..7614988 100644 --- a/src/mmd/PmxReader.py +++ b/src/mmd/PmxReader.py @@ -2,18 +2,16 @@ # import struct import hashlib -from collections import OrderedDict -from mmd.PmxData import PmxModel, Vertex, Material, Bone, Morph, DisplaySlot, RigidBody, Joint +from mmd.PmxData import PmxModel, Bone, RigidBody, Vertex, Material, Morph, DisplaySlot, RigidBody, Joint, Ik, IkLink, Bdef1, Bdef2, Bdef4, Sdef, Qdef # noqa from module.MMath import MRect, MVector3D, MVector4D, MQuaternion, MMatrix4x4 # noqa -from utils.MException import MParseException # noqa from utils.MLogger import MLogger # noqa -from utils.MException import SizingException, MKilledException +from utils.MException import SizingException, MKilledException, MParseException logger = MLogger(__name__, level=1) -class PmxReader(): +class PmxReader: def __init__(self, file_path, is_check=True): self.file_path = file_path self.is_check = is_check @@ -298,7 +296,7 @@ def read_data(self): bone.external_key = self.read_int(4) if bone.getIkFlag(): - bone.ik = Bone.Ik( + bone.ik = Ik( target_index=self.read_bone_index_size(), loop=self.read_int(4), limit_radian=self.read_float() @@ -307,7 +305,7 @@ def read_data(self): # IKリンク取得 for _ in range(self.read_int(4)): - link = Bone.IkLink( + link = IkLink( self.read_bone_index_size(), self.read_int(1) ) @@ -368,7 +366,7 @@ def read_data(self): # 右足底実体ボーン right_sole_vertex = None if "右足先EX" in pmx.bones: - right_sole_vertex = Vertex(-1, MVector3D(pmx.bones["右足先EX"].position.x(), 0, pmx.bones["右足先EX"].position.z()), MVector3D(), [], [], Vertex.Bdef1(-1), -1) + right_sole_vertex = Vertex(-1, MVector3D(pmx.bones["右足先EX"].position.x(), 0, pmx.bones["右足先EX"].position.z()), MVector3D(), [], [], Bdef1(-1), -1) elif "右足IK" in pmx.bones: right_sole_vertex = pmx.get_sole_vertex("右") @@ -383,7 +381,7 @@ def read_data(self): # 左足底実体ボーン left_sole_vertex = None if "左足先EX" in pmx.bones: - left_sole_vertex = Vertex(-1, MVector3D(pmx.bones["左足先EX"].position.x(), 0, pmx.bones["左足先EX"].position.z()), MVector3D(), [], [], Vertex.Bdef1(-1), -1) + left_sole_vertex = Vertex(-1, MVector3D(pmx.bones["左足先EX"].position.x(), 0, pmx.bones["左足先EX"].position.z()), MVector3D(), [], [], Bdef1(-1), -1) elif "左足IK" in pmx.bones: left_sole_vertex = pmx.get_sole_vertex("左") @@ -396,7 +394,7 @@ def read_data(self): if "右足IK" in pmx.bones: # 右足IK底実体ボーン - right_ik_sole_vertex = Vertex(-1, MVector3D(pmx.bones["右足IK"].position.x(), 0, pmx.bones["右足IK"].position.z()), MVector3D(), [], [], Vertex.Bdef1(-1), -1) + right_ik_sole_vertex = Vertex(-1, MVector3D(pmx.bones["右足IK"].position.x(), 0, pmx.bones["右足IK"].position.z()), MVector3D(), [], [], Bdef1(-1), -1) pmx.right_ik_sole_vertex = right_ik_sole_vertex right_ik_sole_bone = Bone("右足IK底実体", "right ik ik_sole entity", right_ik_sole_vertex.position.copy(), -1, 0, 0) right_ik_sole_bone.index = len(pmx.bones.keys()) @@ -405,7 +403,7 @@ def read_data(self): if "左足IK" in pmx.bones: # 左足IK底実体ボーン - left_ik_sole_vertex = Vertex(-1, MVector3D(pmx.bones["左足IK"].position.x(), 0, pmx.bones["左足IK"].position.z()), MVector3D(), [], [], Vertex.Bdef1(-1), -1) + left_ik_sole_vertex = Vertex(-1, MVector3D(pmx.bones["左足IK"].position.x(), 0, pmx.bones["左足IK"].position.z()), MVector3D(), [], [], Bdef1(-1), -1) pmx.left_ik_sole_vertex = left_ik_sole_vertex left_ik_sole_bone = Bone("左足IK底実体", "left ik ik_sole entity", left_ik_sole_vertex.position.copy(), -1, 0, 0) left_ik_sole_bone.index = len(pmx.bones.keys()) @@ -414,7 +412,7 @@ def read_data(self): if "右足IK親" in pmx.bones: # 右足IK親底実体ボーン - right_ik_sole_vertex = Vertex(-1, MVector3D(pmx.bones["右足IK親"].position.x(), 0, pmx.bones["右足IK親"].position.z()), MVector3D(), [], [], Vertex.Bdef1(-1), -1) + right_ik_sole_vertex = Vertex(-1, MVector3D(pmx.bones["右足IK親"].position.x(), 0, pmx.bones["右足IK親"].position.z()), MVector3D(), [], [], Bdef1(-1), -1) pmx.right_ik_sole_vertex = right_ik_sole_vertex right_ik_sole_bone = Bone("右足IK親底実体", "right ik ik_sole entity", right_ik_sole_vertex.position.copy(), -1, 0, 0) right_ik_sole_bone.index = len(pmx.bones.keys()) @@ -423,7 +421,7 @@ def read_data(self): if "左足IK親" in pmx.bones: # 左足IK親底実体ボーン - left_ik_sole_vertex = Vertex(-1, MVector3D(pmx.bones["左足IK親"].position.x(), 0, pmx.bones["左足IK親"].position.z()), MVector3D(), [], [], Vertex.Bdef1(-1), -1) + left_ik_sole_vertex = Vertex(-1, MVector3D(pmx.bones["左足IK親"].position.x(), 0, pmx.bones["左足IK親"].position.z()), MVector3D(), [], [], Bdef1(-1), -1) pmx.left_ik_sole_vertex = left_ik_sole_vertex left_ik_sole_bone = Bone("左足IK親底実体", "left ik ik_sole entity", left_ik_sole_vertex.position.copy(), -1, 0, 0) left_ik_sole_bone.index = len(pmx.bones.keys()) @@ -432,7 +430,7 @@ def read_data(self): # 首根元ボーン if "左肩" in pmx.bones and "右肩" in pmx.bones: - neck_base_vertex = Vertex(-1, (pmx.bones["左肩"].position + pmx.bones["右肩"].position) / 2, MVector3D(), [], [], Vertex.Bdef1(-1), -1) + neck_base_vertex = Vertex(-1, (pmx.bones["左肩"].position + pmx.bones["右肩"].position) / 2, MVector3D(), [], [], Bdef1(-1), -1) neck_base_vertex.position.setX(0) neck_base_bone = Bone("首根元", "base of neck", neck_base_vertex.position.copy(), -1, 0, 0) @@ -450,7 +448,7 @@ def read_data(self): # 首根元2ボーン if "左腕" in pmx.bones and "右腕" in pmx.bones: - neck_base2_vertex = Vertex(-1, (pmx.bones["左腕"].position + pmx.bones["右腕"].position) / 2, MVector3D(), [], [], Vertex.Bdef1(-1), -1) + neck_base2_vertex = Vertex(-1, (pmx.bones["左腕"].position + pmx.bones["右腕"].position) / 2, MVector3D(), [], [], Bdef1(-1), -1) neck_base2_vertex.position.setX(0) neck_base2_bone = Bone("首根元2", "base of neck", neck_base2_vertex.position.copy(), -1, 0, 0) @@ -542,12 +540,13 @@ def read_data(self): finger_tail_pos = finger_tail_vertex.position.copy() finger_tail_bone = Bone(to_joint_name, "", finger_tail_pos, -1, 0, 0) finger_tail_bone.index = len(pmx.bones.keys()) + finger_tail_bone.parent_index = pmx.bones[end_joint_name].index pmx.bones[finger_tail_bone.name] = finger_tail_bone pmx.bone_indexes[finger_tail_bone.index] = finger_tail_bone.name # 足中間ボーン if "左足" in pmx.bones and "右足" in pmx.bones: - leg_center_vertex = Vertex(-1, (pmx.bones["左足"].position + pmx.bones["右足"].position) / 2, MVector3D(), [], [], Vertex.Bdef1(-1), -1) + leg_center_vertex = Vertex(-1, (pmx.bones["左足"].position + pmx.bones["右足"].position) / 2, MVector3D(), [], [], Bdef1(-1), -1) leg_center_vertex.position.setX(0) leg_center_bone = Bone("足中間", "base of neck", leg_center_vertex.position.copy(), -1, 0, 0) @@ -567,7 +566,7 @@ def read_data(self): self.calc_bone_length(pmx.bones, pmx.bone_indexes) # 操作パネル (PMD:カテゴリ) 1:眉(左下) 2:目(左上) 3:口(右上) 4:その他(右下) - morphs_by_panel = OrderedDict() + morphs_by_panel = {} morphs_by_panel[2] = [] # 目 morphs_by_panel[1] = [] # 眉 morphs_by_panel[3] = [] # 口 @@ -768,28 +767,28 @@ def calc_bone_length(self, bones, bone_indexes): if k in ["左足IK", "右足IK", "右足IK親", "左足IK親"] and v.getIkFlag(): # 足IKの場合、ひざボーンの位置を採用する knee_pos = MVector3D(0, 0, 0) - for l in v.ik.link: - logger.test("k %s, link %s", k, l) - if l.bone_index in bone_indexes and "ひざ" in bones[bone_indexes[l.bone_index]].name: + for lk in v.ik.link: + logger.test("k %s, link %s", k, lk) + if lk.bone_index in bone_indexes and "ひざ" in bones[bone_indexes[lk.bone_index]].name: # 存在するボーンで、大きい方を採用 - knee_pos = bones[bone_indexes[l.bone_index]].position - v.len = knee_pos.length() + knee_pos = bones[bone_indexes[lk.bone_index]].position + v.len_1d = knee_pos.length() elif k in ["左つま先IK", "右つま先IK"] and v.getIkFlag(): # IKの場合、リンクボーンの離れている方を採用する farer_pos = MVector3D(0, 0, 0) - for l in v.ik.link: - logger.test("k %s, link %s", k, l) - if l.bone_index in bone_indexes and farer_pos.length() < bones[bone_indexes[l.bone_index]].position.length(): + for lk in v.ik.link: + logger.test("k %s, link %s", k, lk) + if lk.bone_index in bone_indexes and farer_pos.length() < bones[bone_indexes[lk.bone_index]].position.length(): # 存在するボーンで、大きい方を採用 - farer_pos = bones[bone_indexes[l.bone_index]].position - logger.test("farer: %s", bones[bone_indexes[l.bone_index]].position) + farer_pos = bones[bone_indexes[lk.bone_index]].position + logger.test("farer: %s", bones[bone_indexes[lk.bone_index]].position) # 最も大きな値(離れている)のを採用 - v.len = farer_pos.length() + v.len_1d = farer_pos.length() elif k in ["グルーブ", "センター", "腰"]: # 親がグルーブの場合、センターとの連動は行わない - v.len = v.position.length() + v.len_1d = v.position.length() if k == "センター": v.len_3d = MVector3D(1, v.position.length(), 1) else: @@ -799,19 +798,19 @@ def calc_bone_length(self, bones, bone_indexes): if v.parent_index is not None and v.parent_index in bone_indexes and not bone_indexes[v.parent_index] in ["腰", "グルーブ", "センター", "左足IK", "右足IK", "左つま先IK", "右つま先IK", "右足IK親", "左足IK親"]: # 親ボーンを採用 pos = v.position - bones[bone_indexes[v.parent_index]].position - if v.len > 0: + if v.len_1d > 0: # 既にある場合、平均値を求めて設定する - bones[bone_indexes[v.parent_index]].len = (v.len + pos.length()) / 2 + bones[bone_indexes[v.parent_index]].len_1d = (v.len_1d + pos.length()) / 2 bones[bone_indexes[v.parent_index]].len_3d = (v.len_3d + pos) / 2 else: # 0の場合はそのまま追加 - bones[bone_indexes[v.parent_index]].len = pos.length() + bones[bone_indexes[v.parent_index]].len_1d = pos.length() bones[bone_indexes[v.parent_index]].len_3d = pos logger.test("bone: %s, len_3d: %s", bone_indexes[v.parent_index], bones[bone_indexes[v.parent_index]].len_3d) else: # 自分が最親の場合、そのまま長さ - v.len = v.position.length() + v.len_1d = v.position.length() v.len_3d = v.position logger.test("bone: %s, len_3d: %s", v.name, v.len_3d) @@ -882,17 +881,17 @@ def read_deform(self): if deform_type == 0: # BDEF1 - return Vertex.Bdef1(self.read_bone_index_size()) + return Bdef1(self.read_bone_index_size()) elif deform_type == 1: # BDEF2 - return Vertex.Bdef2( + return Bdef2( self.read_bone_index_size(), self.read_bone_index_size(), self.read_float() ) elif deform_type == 2: # BDEF4 - return Vertex.Bdef4( + return Bdef4( self.read_bone_index_size(), self.read_bone_index_size(), self.read_bone_index_size(), @@ -904,7 +903,7 @@ def read_deform(self): ) elif deform_type == 3: # SDEF - return Vertex.Sdef( + return Sdef( self.read_bone_index_size(), self.read_bone_index_size(), self.read_float(), @@ -914,7 +913,7 @@ def read_deform(self): ) elif deform_type == 4: # QDEF - return Vertex.Qdef( + return Qdef( self.read_bone_index_size(), self.read_bone_index_size(), self.read_float(), diff --git a/src/mmd/VmdData.pxd b/src/mmd/VmdData.pxd index f75553a..ec690cc 100644 --- a/src/mmd/VmdData.pxd +++ b/src/mmd/VmdData.pxd @@ -2,6 +2,28 @@ # from module.MMath cimport MRect, MVector2D, MVector3D, MVector4D, MQuaternion, MMatrix4x4 # noqa + +cdef class LowPassFilter: + cdef double __y + cdef double __s + cdef double __alpha + cdef __setAlpha(self, double alpha) + cdef double c__call__(self, double value, double timestamp, double alpha) + cdef double lastValue(self) + cdef double skip(self, double value) + +cdef class OneEuroFilter: + cdef double __freq + cdef double __mincutoff + cdef double __beta + cdef double __dcutoff + cdef LowPassFilter __x + cdef LowPassFilter __dx + cdef double __lasttime + cdef double __alpha(self, double cutoff) + cdef double c__call__(self, double x, double timestamp) + cdef c_skip(self, double x, str timestamp) + cdef class VmdBoneFrame: cdef public str name cdef public bytes bname diff --git a/src/mmd/VmdData.pyx b/src/mmd/VmdData.pyx index 0bdf1de..3b2fac8 100644 --- a/src/mmd/VmdData.pyx +++ b/src/mmd/VmdData.pyx @@ -124,7 +124,6 @@ cdef class VmdBoneFrame: self.fno = fno self.position = MVector3D() self.rotation = MQuaternion() - self.org_position = MVector3D() self.org_rotation = MQuaternion() self.interpolation = [20, 20, 0, 0, 20, 20, 20, 20, 107, 107, 107, 107, 107, 107, 107, 107, 20, 20, 20, 20, 20, 20, 20, 107, 107, 107, 107, 107, 107, 107, 107, 0, 20, 20, 20, 20, 20, 20, 107, 107, 107, 107, 107, 107, 107, 107, 0, 0, 20, 20, 20, 20, 20, 107, 107, 107, 107, 107, 107, 107, 107, 0, 0, 0] # noqa self.org_interpolation = [20, 20, 0, 0, 20, 20, 20, 20, 107, 107, 107, 107, 107, 107, 107, 107, 20, 20, 20, 20, 20, 20, 20, 107, 107, 107, 107, 107, 107, 107, 107, 0, 20, 20, 20, 20, 20, 20, 107, 107, 107, 107, 107, 107, 107, 107, 0, 0, 20, 20, 20, 20, 20, 107, 107, 107, 107, 107, 107, 107, 107, 0, 0, 0] # noqa @@ -145,7 +144,6 @@ cdef class VmdBoneFrame: bf.bname = self.bname bf.position = self.position.copy() bf.rotation = self.rotation.copy() - bf.org_position = self.org_position.copy() bf.org_rotation = self.org_rotation.copy() bf.interpolation = cPickle.loads(cPickle.dumps(self.interpolation, -1)) bf.key = self.key @@ -864,7 +862,6 @@ cdef class VmdMotion: cdef VmdBoneFrame regist_bf = self.c_calc_bf(bone_name, fno, is_key=False, is_read=False, is_reset_interpolation=True) regist_bf.position = bf.position.copy() regist_bf.rotation = bf.rotation.copy() - regist_bf.org_position = bf.org_position.copy() regist_bf.org_rotation = bf.org_rotation.copy() if copy_interpolation: regist_bf.interpolation = cPickle.loads(cPickle.dumps(bf.interpolation, -1)) @@ -1214,7 +1211,8 @@ cdef class VmdMotion: for bone_name, bone_frames in self.bones.items(): if bone_name not in ["SIZING_ROOT_BONE", "頭頂", "右つま先実体", "左つま先実体", "右足底辺", "左足底辺", "右足底実体", "左足底実体", "右足IK底実体", "左足IK底実体", "右足IK親底実体", "左足IK親底実体", \ - "首根元", "右腕下延長", "左腕下延長", "右腕垂直", "左腕垂直", "センター実体", "左腕ひじ中間", "右腕ひじ中間", "左ひじ手首中間", "右ひじ手首中間", "左手首実体", "右手首実体"]: + "首根元", "右腕下延長", "左腕下延長", "右腕垂直", "左腕垂直", "センター実体", "左腕ひじ中間", "右腕ひじ中間", "左ひじ手首中間", "右ひじ手首中間", "左手首実体", "右手首実体", \ + "左親指先実体", "左人指先実体", "左中指先実体", "左薬指先実体", "左小指先実体", "右親指先実体", "右人指先実体", "右中指先実体", "右薬指先実体", "右小指先実体"]: # サイジング用ボーンは出力しない target_fnos[bone_name] = self.get_bone_fnos(bone_name, is_key=True) diff --git a/src/mmd/VmdReader.py b/src/mmd/VmdReader.py index fa8dd96..6946d1e 100644 --- a/src/mmd/VmdReader.py +++ b/src/mmd/VmdReader.py @@ -6,14 +6,13 @@ from mmd.VmdData import VmdMotion, VmdBoneFrame, VmdCameraFrame, VmdInfoIk, VmdLightFrame, VmdMorphFrame, VmdShadowFrame, VmdShowIkFrame from module.MMath import MRect, MVector3D, MVector4D, MQuaternion, MMatrix4x4 # noqa -from utils.MException import MParseException # noqa from utils.MLogger import MLogger # noqa -from utils.MException import SizingException, MKilledException +from utils.MException import SizingException, MKilledException, MParseException logger = MLogger(__name__) -class VmdReader(): +class VmdReader: def __init__(self, file_path): self.offset = 0 self.buffer = None @@ -83,8 +82,6 @@ def read_data(self): # 位置X,Y,Z frame.position = self.read_Vector3D() logger.test("frame.position %s", frame.position) - # オリジナルを保持 - frame.org_position = frame.position.copy() # 回転X,Y,Z,scalar frame.rotation = self.read_Quaternion() @@ -96,9 +93,6 @@ def read_data(self): # 補間曲線 frame.interpolation = list(self.unpack(64, "64B", True)) logger.test("interpolation %s", frame.interpolation) - # オリジナルの補間曲線を保持しておく - # frame.org_interpolation = copy.deepcopy(frame.interpolation) - # logger.test("org_interpolation %s", frame.org_interpolation) if bone_name not in motion.bones: # まだ辞書にない場合、配列追加 @@ -288,7 +282,8 @@ def read_data(self): # IK名 ik_bname, ik_name = self.read_text(20) - ik_info.name = ik_bname + ik_info.name = ik_name + ik_info.bname = ik_bname logger.test("ik_info.name %s", ik_name) # モデル表示, 0:OFF, 1:ON diff --git a/src/mmd/VmdWriter.py b/src/mmd/VmdWriter.py index 94178fb..6022f2a 100644 --- a/src/mmd/VmdWriter.py +++ b/src/mmd/VmdWriter.py @@ -40,7 +40,7 @@ def write(self): # bone frames fout.write(struct.pack(' 3: + else: # 3次より多い場合、次数を減らす reduced_curve_list = [] @@ -171,8 +209,8 @@ def join_value_2_bezier(fno: int, bone_name: str, values: list, offset=0, diff_l # リストに追加 reduced_curve_list.append(reduced_curve) - bz_x = [] - bz_y = [] + bz_x = np.empty(0) + bz_y = np.empty(0) for reduced_curve in reduced_curve_list: bz_x = np.append(bz_x, reduced_curve.nodes[0]) @@ -190,7 +228,7 @@ def join_value_2_bezier(fno: int, bone_name: str, values: list, offset=0, diff_l logger.test("joined_curve: %s", joined_curve.nodes) # 全体のキーフレ - bezier_x = np.arange(0, len(values))[1:-1] + bezier_x = np.arange(0, len(values), dtype=np.float)[1:] # 元の2つのベジェ曲線との交点を取得する full_ys = intersect_by_x(full_curve, bezier_x) @@ -200,11 +238,11 @@ def join_value_2_bezier(fno: int, bone_name: str, values: list, offset=0, diff_l reduced_ys = intersect_by_x(joined_curve, bezier_x) logger.test("f: %s, %s, reduced_ys: %s", fno, bone_name, reduced_ys) - # 交点の差を取得する - diff_ys = np.array(full_ys) - np.array(reduced_ys) + # 交点の差を取得する(前後は必ず一致) + diff_ys = np.concatenate([[0], np.array(full_ys) - np.array(reduced_ys)]) # 差が大きい箇所をピックアップする - diff_large = np.where(np.abs(diff_ys) > (diff_limit * (offset + 1)), 1, 0) + diff_large = np.where(np.abs(diff_ys) > (diff_limit * (offset + 1)), 1, 0).astype(np.float) # 差が一定未満である場合、ベジェ曲線をMMD補間曲線に合わせる nodes = joined_curve.nodes @@ -218,36 +256,50 @@ def join_value_2_bezier(fno: int, bone_name: str, values: list, offset=0, diff_l if np.count_nonzero(diff_large) > 0: # 差が大きい箇所がある場合、分割不可 - return None + return (None, np.where(diff_large)[0].tolist()) if not is_fit_bezier_mmd(joined_bz, offset): # 補間曲線がMMD補間曲線内に収まらない場合、NG - return None + + # 差分の大きなところを返す + diff_large = np.where(np.abs(diff_ys) > (diff_limit * 0.5 * (offset + 1)), 1, 0).astype(np.float) + if np.count_nonzero(diff_large) > 0: + return (None, np.where(diff_large)[0].tolist()) + + # 差分の大きなところを返す + diff_large = np.where(np.abs(diff_ys) > 0, 1, 0).astype(np.float) + if np.count_nonzero(diff_large) > 0: + return (None, np.where(diff_large)[0].tolist()) + + return (None, []) # オフセット込みの場合、MMD用補間曲線枠内に収める fit_bezier_mmd(joined_bz) # すべてクリアした場合、補間曲線採用 - return joined_bz + return (joined_bz, []) except Exception as e: # エラーレベルは落として表に出さない logger.debug("ベジェ曲線生成失敗", e) - return None + return (None, []) -def fit_bezier_mmd(bzs): +cdef bint fit_bezier_mmd(list bzs): for bz in bzs: bz.effective() bz.setX(0 if bz.x() < 0 else INTERPOLATION_MMD_MAX if bz.x() > INTERPOLATION_MMD_MAX else bz.x()) bz.setY(0 if bz.y() < 0 else INTERPOLATION_MMD_MAX if bz.y() > INTERPOLATION_MMD_MAX else bz.y()) + return True + # Catmull-Rom曲線の制御点(通過点)をBezier曲線の制御点に変換する # http://defghi1977-onblog.blogspot.com/2014/09/catmull-rombezier.html -def convert_catmullrom_2_bezier(xs: list, ys: list): +cdef tuple convert_catmullrom_2_bezier(np.ndarray xs, np.ndarray ys): - bz_x = [] - bz_y = [] + cdef list bz_x = [] + cdef list bz_y = [] + cdef MVector2D p0, p1, p2, p3, B, C for x0, x1, x2, x3, y0, y1, y2, y3 in zip(xs[:-3], xs[1:-2], xs[2:-1], xs[3:], ys[:-3], ys[1:-2], ys[2:-1], ys[3:]): p0 = None if not x0 and not y0 else MVector2D(x0, y0) @@ -295,12 +347,16 @@ def convert_catmullrom_2_bezier(xs: list, ys: list): bz_x.append(xs[-2]) bz_y.append(ys[-2]) - return bz_x, bz_y + return (np.array(bz_x, dtype=np.float64), np.array(bz_y, dtype=np.float64)) # 指定された複数のXと交わるそれぞれのYを返す -def intersect_by_x(curve, xs): - ys = [] +cdef np.ndarray intersect_by_x(curve, np.ndarray xs): + cdef double x + cdef list ys = [] + cdef np.ndarray[np.float_t, ndim=1] s_vals + cdef np.ndarray[np.float_t, ndim=2] intersections + for x in xs: # 交点を求める為のX線上の直線 line1 = bezier.Curve(np.asfortranarray([[x, x], [-99999, 99999]]), degree=1) @@ -321,7 +377,7 @@ def intersect_by_x(curve, xs): else: ys.append(0) - return ys + return np.array(ys, dtype=np.float) # 補間曲線を求める @@ -330,8 +386,15 @@ def intersect_by_x(curve, xs): # https://shspage.hatenadiary.org/entry/20140625/1403702735 # https://bezier.readthedocs.io/en/stable/python/reference/bezier.curve.html#bezier.curve.Curve.evaluate def evaluate(x1v: int, y1v: int, x2v: int, y2v: int, start: int, now: int, end: int): + return_tuple = c_evaluate(x1v, y1v, x2v, y2v, start, now, end) + return return_tuple[0], return_tuple[1], return_tuple[2] + +cdef tuple c_evaluate(int x1v, int y1v, int x2v, int y2v, int start, int now, int end): if (now - start) == 0 or (end - start) == 0: - return 0, 0, 0 + return (0, 0, 0) + + cdef double x, x1, x2, y1, y2, t, s, ft, y + cdef int i x = (now - start) / (end - start) x1 = x1v / INTERPOLATION_MMD_MAX @@ -342,16 +405,12 @@ def evaluate(x1v: int, y1v: int, x2v: int, y2v: int, start: int, now: int, end: t = 0.5 s = 0.5 + # 二分法 # logger.test("x1: %s, x2: %s, y1: %s, y2: %s, x: %s", x1, x2, y1, y2, x) - for i in range(15): ft = (3 * (s * s) * t * x1) + (3 * s * (t * t) * x2) + (t * t * t) - x # logger.test("i: %s, 4 << i: %s, ft: %s(%s), t: %s, s: %s", i, (4 << i), ft, abs(ft) < 0.00001, t, s) - # lessさんのご指摘によりコメントアウト - # if abs(ft) < 0.00001: - # break - if ft > 0: t -= 1 / (4 << i) else: @@ -363,14 +422,21 @@ def evaluate(x1v: int, y1v: int, x2v: int, y2v: int, start: int, now: int, end: # logger.test("y: %s, t: %s, s: %s", y, t, s) - return x, y, t + return (x, y, t) # 指定されたtになるフレーム番号を取得する def evaluate_by_t(x1v: int, y1v: int, x2v: int, y2v: int, start: int, end: int, t: float): + return_tuple = c_evaluate_by_t(x1v, y1v, x2v, y2v, start, end, t) + return return_tuple[0], return_tuple[1], return_tuple[2] + +cdef tuple c_evaluate_by_t(int x1v, int y1v, int x2v, int y2v, int start, int end, double t): if (end - start) <= 1: # 差が1以内の場合、終了 - return start, 0, t + return (start, 0, t) + + cdef double x1, x2, y1, y2 + cdef int fno x1 = x1v / INTERPOLATION_MMD_MAX x2 = x2v / INTERPOLATION_MMD_MAX @@ -386,7 +452,7 @@ def evaluate_by_t(x1v: int, y1v: int, x2v: int, y2v: int, start: int, end: int, # xに相当するフレーム番号 fno = int(round_integer(start + ((end - start) * es[0, 0]))) - return fno, es[1, 0], t + return (fno, es[1, 0], t) # 3次ベジェ曲線の分割 @@ -395,7 +461,12 @@ def split_bezier_mmd(x1v: int, y1v: int, x2v: int, y2v: int, start: int, now: in return 0, 0, 0, False, False, LINEAR_MMD_INTERPOLATION, LINEAR_MMD_INTERPOLATION # 3次ベジェ曲線を分割する - x, y, t, before_bz, after_bz = split_bezier(x1v, y1v, x2v, y2v, start, now, end) + return_tuple = split_bezier(x1v, y1v, x2v, y2v, start, now, end) + x = return_tuple[0] + y = return_tuple[1] + t = return_tuple[2] + before_bz = return_tuple[3] + after_bz = return_tuple[4] # ベジェ曲線の値がMMD用に合っているかを加味して返す return x, y, t, is_fit_bezier_mmd(before_bz), is_fit_bezier_mmd(after_bz), before_bz, after_bz @@ -417,52 +488,55 @@ def is_fit_bezier_mmd(bz: list, offset=0): # 3次ベジェ曲線の分割 # http://geom.web.fc2.com/geometry/bezier/cut-cb.html -def split_bezier(x1v: int, y1v: int, x2v: int, y2v: int, start: int, now: int, end: int): +cdef tuple split_bezier(int x1v, int y1v, int x2v, int y2v, int start, int now, int end): # 補間曲線の進んだ時間分を求める - x, y, t = evaluate(x1v, y1v, x2v, y2v, start, now, end) - - A = MVector2D(0.0, 0.0) - B = MVector2D(x1v / INTERPOLATION_MMD_MAX, y1v / INTERPOLATION_MMD_MAX) - C = MVector2D(x2v / INTERPOLATION_MMD_MAX, y2v / INTERPOLATION_MMD_MAX) - D = MVector2D(1.0, 1.0) - - E = A * (1 - t) + B * t - F = B * (1 - t) + C * t - G = C * (1 - t) + D * t - H = E * (1 - t) + F * t - I = F * (1 - t) + G * t # noqa - J = H * (1 - t) + I * t + return_tuple = c_evaluate(x1v, y1v, x2v, y2v, start, now, end) + cdef double x = return_tuple[0] + cdef double y = return_tuple[1] + cdef double t = return_tuple[2] + + cdef MVector2D A = MVector2D(0.0, 0.0) + cdef MVector2D B = MVector2D(x1v / INTERPOLATION_MMD_MAX, y1v / INTERPOLATION_MMD_MAX) + cdef MVector2D C = MVector2D(x2v / INTERPOLATION_MMD_MAX, y2v / INTERPOLATION_MMD_MAX) + cdef MVector2D D = MVector2D(1.0, 1.0) + + cdef MVector2D E = A * (1 - t) + B * t + cdef MVector2D F = B * (1 - t) + C * t + cdef MVector2D G = C * (1 - t) + D * t + cdef MVector2D H = E * (1 - t) + F * t + cdef MVector2D I = F * (1 - t) + G * t # noqa + cdef MVector2D J = H * (1 - t) + I * t # 新たな4つのベジェ曲線の制御点は、A側がAEHJ、C側がJIGDとなる。 # スケーリング - beforeBz = scale_bezier(A, E, H, J) - afterBz = scale_bezier(J, I, G, D) + cdef list beforeBz = scale_bezier(A, E, H, J) + cdef list afterBz = scale_bezier(J, I, G, D) - return x, y, t, beforeBz, afterBz + return (x, y, t, beforeBz, afterBz) # 分割したベジェのスケーリング -def scale_bezier(p1: MVector2D, p2: MVector2D, p3: MVector2D, p4: MVector2D): - diff = p4 - p1 +cdef list scale_bezier(MVector2D p1, MVector2D p2, MVector2D p3, MVector2D p4): + cdef MVector2D diff = p4 - p1 # nan対策 - s1 = scale_bezier_point(p1, p1, diff) - s2 = scale_bezier_point(p2, p1, diff) - s3 = scale_bezier_point(p3, p1, diff) - s4 = scale_bezier_point(p4, p1, diff) + cdef MVector2D s1 = scale_bezier_point(p1, p1, diff) + cdef MVector2D s2 = scale_bezier_point(p2, p1, diff) + cdef MVector2D s3 = scale_bezier_point(p3, p1, diff) + cdef MVector2D s4 = scale_bezier_point(p4, p1, diff) - bs1 = round_bezier_mmd(s1) - bs2 = round_bezier_mmd(s2) - bs3 = round_bezier_mmd(s3) - bs4 = round_bezier_mmd(s4) + cdef MVector2D bs1 = round_bezier_mmd(s1) + cdef MVector2D bs2 = round_bezier_mmd(s2) + cdef MVector2D bs3 = round_bezier_mmd(s3) + cdef MVector2D bs4 = round_bezier_mmd(s4) return [bs1, bs2, bs3, bs4] # nan対策を加味したベジェ曲線の点算出 -def scale_bezier_point(pn: MVector2D, p1: MVector2D, diff: MVector2D): - s = (pn - p1) / diff +cdef MVector2D scale_bezier_point(MVector2D pn, MVector2D p1, MVector2D diff): + cdef MVector2D s = (pn - p1) / diff # logger.test("diff: %s", diff) # logger.test("(pn-p1): %s", (pn-p1)) @@ -475,8 +549,8 @@ def scale_bezier_point(pn: MVector2D, p1: MVector2D, diff: MVector2D): # ベジェ曲線をMMD用の数値に丸める -def round_bezier_mmd(target: MVector2D): - t2 = MVector2D() +cdef MVector2D round_bezier_mmd(MVector2D target): + cdef MVector2D t2 = MVector2D() # XとYをそれぞれ整数(0-127)に丸める t2.setX(round_integer(target.x() * INTERPOLATION_MMD_MAX)) @@ -485,9 +559,9 @@ def round_bezier_mmd(target: MVector2D): return t2 -def round_integer(t: float): +cdef int round_integer(double t): # 一旦整数部にまで持ち上げる - t2 = t * 1000000 + cdef double t2 = t * 1000000 # pythonは偶数丸めなので、整数部で丸めた後、元に戻す return round(round(t2, -6) / 1000000) diff --git a/vmdising_np64.spec b/vmdising_np64.spec index 8abbf95..5378dc9 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,7 +26,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β50_64bit', + name='VmdSizing_5.01_β51_64bit', debug=False, bootloader_ignore_signals=False, strip=False, From 0cd5d48851d3a5bcb1b76645653943af5dd08709 Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Sun, 27 Sep 2020 01:38:11 +0900 Subject: [PATCH 18/37] =?UTF-8?q?5.01=5F=CE=B252?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VmdSizing_5.01_β52 miumiu ・サイジング結果出力時(VMD出力時)に出力に失敗した場合のエラーメッセージ出力修正 ・処理対象データパッキングクラス ・サービス用ユーティリティ ・ロギングクラス  ・Cythonに戻した --- src/executor.py | 2 +- src/module/MMath.pyx | 20 +- src/module/MOptions.pxd | 98 +++++ src/module/MOptions.py | 394 ----------------- src/module/MOptions.pyx | 406 ++++++++++++++++++ src/module/MParams.pxd | 6 + src/module/{MParams.py => MParams.pyx} | 6 +- src/module/StdoutQueue.py | 38 -- src/setup.bat | 4 +- src/setup_ext.py | 4 +- src/utils/MLogger.py | 14 +- src/utils/MServiceUtils.pxd | 30 ++ .../{MServiceUtils.py => MServiceUtils.pyx} | 349 ++++++++------- vmdising_np64.spec | 2 +- 14 files changed, 780 insertions(+), 593 deletions(-) create mode 100644 src/module/MOptions.pxd delete mode 100644 src/module/MOptions.py create mode 100644 src/module/MOptions.pyx create mode 100644 src/module/MParams.pxd rename src/module/{MParams.py => MParams.pyx} (97%) delete mode 100644 src/module/StdoutQueue.py create mode 100644 src/utils/MServiceUtils.pxd rename src/utils/{MServiceUtils.py => MServiceUtils.pyx} (64%) diff --git a/src/executor.py b/src/executor.py index f61afa6..ac52d38 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00_β51" +VERSION_NAME = "ver5.00_β52" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/module/MMath.pyx b/src/module/MMath.pyx index dc57d5f..fa0aac2 100644 --- a/src/module/MMath.pyx +++ b/src/module/MMath.pyx @@ -1463,13 +1463,19 @@ cdef class MMatrix4x4: # 平行移動行列 cpdef translate(self, MVector3D vec3): - cdef np.ndarray[DTYPE_FLOAT_t, ndim=2] vec_mat = np.tile(np.array([vec3.x(), vec3.y(), vec3.z()]), (4, 1)) + cdef np.ndarray[DTYPE_FLOAT_t, ndim=2] vec_mat = np.array([[vec3.x(), vec3.y(), vec3.z()], + [vec3.x(), vec3.y(), vec3.z()], + [vec3.x(), vec3.y(), vec3.z()], + [vec3.x(), vec3.y(), vec3.z()]], dtype=np.float64) cdef np.ndarray[DTYPE_FLOAT_t, ndim=2] data_mat = self.__data[:, :3] * vec_mat self.__data[:, 3] += np.sum(data_mat, axis=1) # 縮尺行列 cpdef scale(self, MVector3D vec3): - cdef np.ndarray[DTYPE_FLOAT_t, ndim=2] vec_mat = np.tile(np.array([vec3.x(), vec3.y(), vec3.z()]), (4, 1)) + cdef np.ndarray[DTYPE_FLOAT_t, ndim=2] vec_mat = np.array([[vec3.x(), vec3.y(), vec3.z()], + [vec3.x(), vec3.y(), vec3.z()], + [vec3.x(), vec3.y(), vec3.z()], + [vec3.x(), vec3.y(), vec3.z()]], dtype=np.float64) self.__data[:, :3] *= vec_mat # 単位行列 @@ -1643,7 +1649,10 @@ cdef class MMatrix4x4: return v2 cpdef MVector3D mul_MVector3D(self, MVector3D other): - cdef np.ndarray[DTYPE_FLOAT_t, ndim=2] vec_mat = np.tile(np.array([other.x(), other.y(), other.z()]), (4, 1)) + cdef np.ndarray[DTYPE_FLOAT_t, ndim=2] vec_mat = np.array([[other.x(), other.y(), other.z()], + [other.x(), other.y(), other.z()], + [other.x(), other.y(), other.z()], + [other.x(), other.y(), other.z()]], dtype=np.float64) cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] data_sum = np.sum(vec_mat * self.__data[:, :3], axis=1) + self.__data[:, 3] cdef DTYPE_FLOAT_t x = data_sum[0] @@ -1659,7 +1668,10 @@ cdef class MMatrix4x4: return MVector3D(x / w, y / w, z / w) cpdef MVector4D mul_MVector4D(self, MVector4D other): - cdef np.ndarray[DTYPE_FLOAT_t, ndim=2] vec_mat = np.tile(np.array([other.x(), other.y(), other.z(), other.w()]), (4, 1)) + cdef np.ndarray[DTYPE_FLOAT_t, ndim=2] vec_mat = np.array([[other.x(), other.y(), other.z(), other.w()], + [other.x(), other.y(), other.z(), other.w()], + [other.x(), other.y(), other.z(), other.w()], + [other.x(), other.y(), other.z(), other.w()]], dtype=np.float64) cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] data_sum = np.sum(vec_mat * self.__data, axis=1) cdef DTYPE_FLOAT_t x = data_sum[0] diff --git a/src/module/MOptions.pxd b/src/module/MOptions.pxd new file mode 100644 index 0000000..95a24ba --- /dev/null +++ b/src/module/MOptions.pxd @@ -0,0 +1,98 @@ +# -*- coding: utf-8 -*- +# +import os +import sys +import argparse + +from libcpp cimport list, str, dict, float, int + +from mmd.PmxData cimport PmxModel, Bone +from mmd.VmdData cimport VmdMotion, VmdBoneFrame +from module.MParams cimport BoneLinks # noqa +from module.MMath cimport MRect, MVector2D, MVector3D, MVector4D, MQuaternion, MMatrix4x4 # noqa + +from mmd.PmxReader import PmxReader +from mmd.VmdReader import VmdReader +from mmd.VpdReader import VpdReader +from mmd.PmxData import Vertex, Material, Morph, DisplaySlot, RigidBody, Joint # noqa +from mmd.VmdData import VmdCameraFrame, VmdInfoIk, VmdLightFrame, VmdMorphFrame, VmdShadowFrame, VmdShowIkFrame # noqa +from utils import MFileUtils +from utils.MException import SizingException +from utils.MLogger import MLogger # noqa + +logger = MLogger(__name__) + + +cdef class MOptions(): + cdef public str version_name + cdef public int logging_level + cdef public int max_workers + cdef public list data_set_list + cdef public MArmProcessOptions arm_options + cdef public VmdMotion camera_motion + cdef public str camera_output_vmd_path + cdef public object monitor + cdef public bint is_file + cdef public str outout_datetime + cdef public int total_process + cdef public int now_process + cdef public object total_process_ctrl + cdef public object now_process_ctrl + cdef public dict tree_process_dict + +cdef c_parse(str version_name) + +cdef class MOptionsDataSet(): + cdef public VmdMotion motion + cdef public PmxModel org_model + cdef public PmxModel rep_model + cdef public str output_vmd_path + cdef public bint detail_stance_flg + cdef public bint twist_flg + cdef public list morph_list + cdef public PmxModel camera_org_model + cdef public double camera_offset_y + cdef public list selected_stance_details + + cdef public VmdMotion org_motion + cdef public dict test_params + cdef public bint full_arms + + # 本来の足IKの比率 + cdef public double original_xz_ratio + cdef public double original_y_ratio + + # 実際に計算に使う足IKの比率 + cdef public double xz_ratio + cdef public double y_ratio + + +cdef class MArmProcessOptions(): + cdef public bint avoidance + cdef public dict avoidance_target_list + cdef public bint alignment + cdef public bint alignment_finger_flg + cdef public bint alignment_floor_flg + cdef public double alignment_distance_wrist + cdef public double alignment_distance_finger + cdef public double alignment_distance_floor + cdef public bint arm_check_skip_flg + + +cdef class MSmoothOptions(): + cdef public str version_name + cdef public int logging_level + cdef public int max_workers + cdef public VmdMotion motion + cdef public PmxModel model + cdef public str output_path + cdef public int loop_cnt + cdef public int interpolation + cdef public list bone_list + cdef public object monitor + cdef public bint is_file + cdef public str outout_datetime + +cdef c_smooth_parse(str version_name) + + diff --git a/src/module/MOptions.py b/src/module/MOptions.py deleted file mode 100644 index ef4f4ee..0000000 --- a/src/module/MOptions.py +++ /dev/null @@ -1,394 +0,0 @@ -# -*- coding: utf-8 -*- -# -import os -import sys -import argparse - -from mmd.PmxReader import PmxReader -from mmd.VmdReader import VmdReader -from mmd.VpdReader import VpdReader -from module.MMath import MRect, MVector3D, MVector4D, MQuaternion, MMatrix4x4 # noqa -from utils import MFileUtils -from utils.MException import SizingException -from utils.MLogger import MLogger # noqa - -logger = MLogger(__name__) - - -class MOptions(): - - def __init__(self, version_name, logging_level, max_workers, data_set_list, arm_options, \ - camera_motion, camera_output_vmd_path, monitor, is_file, outout_datetime, \ - total_process, now_process, total_process_ctrl, now_process_ctrl, tree_process_dict): - self.version_name = version_name - self.logging_level = logging_level - self.max_workers = max_workers - self.data_set_list = data_set_list - self.arm_options = arm_options - self.camera_motion = camera_motion - self.camera_output_vmd_path = camera_output_vmd_path - self.monitor = monitor - self.is_file = is_file - self.outout_datetime = outout_datetime - self.total_process = total_process - self.now_process = now_process - self.total_process_ctrl = total_process_ctrl - self.now_process_ctrl = now_process_ctrl - self.tree_process_dict = tree_process_dict - - # 複数件のファイルセットの足IKの比率を再設定する - def calc_leg_ratio(self): - # まず一番小さいXZ比率と一番大きいXZ比率を取得する - min_xz_ratio = 99999999999 - max_xz_ratio = -99999999999 - for data_set_idx, data_set in enumerate(self.data_set_list): - if data_set.original_xz_ratio < min_xz_ratio: - min_xz_ratio = data_set.original_xz_ratio - - if data_set.original_xz_ratio > max_xz_ratio: - max_xz_ratio = data_set.original_xz_ratio - - # XZ比率の差(差分の1.2倍をリミットとする) - total_xz_ratio = min((min_xz_ratio + ((max_xz_ratio - min_xz_ratio) / 2)), 1.2) - logger.test("total_xz_ratio: %s", total_xz_ratio) - - logger.info("") - - log_txt = "足の長さの比率 ---------\n" - - for data_set_idx, data_set in enumerate(self.data_set_list): - if len(self.data_set_list) > 1: - # XZ比率は合計から導き出した比率 - data_set.xz_ratio = total_xz_ratio - data_set.y_ratio = data_set.original_y_ratio - else: - # セットが1件(一人モーションの場合はそのまま) - data_set.xz_ratio = data_set.original_xz_ratio - data_set.y_ratio = data_set.original_y_ratio - - log_txt = "{0}【No.{1}】 xz: {2}, y: {3} (元: xz: {4})\n".format(log_txt, (data_set_idx + 1), data_set.xz_ratio, data_set.y_ratio, data_set.original_xz_ratio) - - logger.info(log_txt) - - @classmethod - def parse(cls, version_name: str): - parser = argparse.ArgumentParser() - parser.add_argument("--motion_path", required=True, type=(lambda x: list(map(str, x.split(';'))))) - parser.add_argument("--org_model_path", required=True, type=(lambda x: list(map(str, x.split(';'))))) - parser.add_argument("--rep_model_path", required=True, type=(lambda x: list(map(str, x.split(';'))))) - parser.add_argument("--detail_stance_flg", required=True, type=(lambda x: list(map(int, x.split(';'))))) - parser.add_argument("--twist_flg", required=True, type=(lambda x: list(map(int, x.split(';'))))) - parser.add_argument("--arm_process_flg_avoidance", type=int, default=0) - parser.add_argument("--avoidance_target_list", default=[], type=(lambda x: list(map(str, x.split(';'))))) - parser.add_argument("--arm_process_flg_alignment", type=int, default=0) - parser.add_argument("--alignment_finger_flg", type=int, default=0) - parser.add_argument("--alignment_floor_flg", type=int, default=0) - parser.add_argument("--alignment_distance_wrist", type=float, default=1.7) - parser.add_argument("--alignment_distance_finger", type=float, default=1.4) - parser.add_argument("--alignment_distance_floor", type=float, default=1.8) - parser.add_argument("--arm_check_skip_flg", type=int, default=0) - parser.add_argument("--camera_motion_path", type=str, default="") - parser.add_argument("--camera_org_model_path", default=[], type=(lambda x: list(map(str, x.split(';'))))) - parser.add_argument("--camera_offset_y", default=[], type=(lambda x: list(map(str, x.split(';'))))) - parser.add_argument("--verbose", type=int, default=20) - - args = parser.parse_args() - - # ログディレクトリ作成 - os.makedirs("log", exist_ok=True) - - MLogger.initialize(level=args.verbose, is_file=True) - - try: - arm_process_flg_avoidance = True if args.arm_process_flg_avoidance == 1 else False - arm_process_flg_alignment = True if args.arm_process_flg_alignment == 1 else False - alignment_finger_flg = True if args.alignment_finger_flg == 1 else False - alignment_floor_flg = True if args.alignment_floor_flg == 1 else False - arm_check_skip_flg = True if args.arm_check_skip_flg == 1 else False - - arm_options = MArmProcessOptions( - arm_process_flg_avoidance, \ - {0: [(a.strip() if len(a.strip()) > 0 else "") for a in args.avoidance_target_list]}, \ - arm_process_flg_alignment, \ - alignment_finger_flg, \ - alignment_floor_flg, \ - args.alignment_distance_wrist, \ - args.alignment_distance_finger, \ - args.alignment_distance_floor, \ - arm_check_skip_flg - ) - - # 元モデルが未指定の場合、空で処理する - if not args.camera_org_model_path or (len(args.camera_org_model_path) == 1 and len(args.camera_org_model_path[0]) == 0): - args.camera_org_model_path = [] - for org_path in args.org_model_path: - args.camera_org_model_path.append("") - - # オフセットYが未指定の場合、0で処理する - if not args.camera_offset_y or (len(args.camera_offset_y) == 1 and len(args.camera_offset_y[0]) == 0): - args.camera_offset_y = [] - for org_path in args.org_model_path: - args.camera_offset_y.append(0) - - data_set_list = [] - for set_no, (motion_path, org_model_path, rep_model_path, detail_stance_flg_val, twist_flg_val, camera_org_model_path, camera_offset_y) in enumerate( \ - zip(args.motion_path, args.org_model_path, args.rep_model_path, args.detail_stance_flg, args.twist_flg, args.camera_org_model_path, \ - args.camera_offset_y)): # noqa - - display_set_no = "【No.{0}】".format(set_no + 1) - - # モーションパス -------- - logger.info("%s 調整対象モーションVMD/VPDファイル 読み込み開始", display_set_no) - - file_name, input_ext = os.path.splitext(os.path.basename(motion_path)) - if input_ext.lower() == ".vmd": - motion_reader = VmdReader(motion_path) - elif input_ext.lower() == ".vpd": - motion_reader = VpdReader(motion_path) - else: - raise SizingException("{0}.motion_path 読み込み失敗(拡張子不正): {1}".format(display_set_no, os.path.basename(motion_path))) - - motion = motion_reader.read_data() - - logger.info("%s 調整対象モーションVMD/VPDファイル 読み込み成功 %s", display_set_no, os.path.basename(motion_path)) - - # 元モデル ---------- - logger.info("%s モーション作成元モデルPMXファイル 読み込み開始", display_set_no) - - file_name, input_ext = os.path.splitext(os.path.basename(org_model_path)) - if input_ext.lower() == ".pmx": - org_model_reader = PmxReader(org_model_path) - else: - raise SizingException("{0}.org_model_path 読み込み失敗(拡張子不正): {1}".format(display_set_no, os.path.basename(org_model_path))) - - org_model = org_model_reader.read_data() - - logger.info("%s モーション作成元モデルPMXファイル 読み込み成功 %s", display_set_no, os.path.basename(org_model_path)) - - # 先モデル ---------- - logger.info("%s モーション変換先モデルPMXファイル 読み込み開始", display_set_no) - - file_name, input_ext = os.path.splitext(os.path.basename(rep_model_path)) - if input_ext.lower() == ".pmx": - rep_model_reader = PmxReader(rep_model_path) - else: - raise SizingException("{0}.rep_model_path 読み込み失敗(拡張子不正): {1}".format(display_set_no, os.path.basename(rep_model_path))) - - rep_model = rep_model_reader.read_data() - - logger.info("%s モーション変換先モデルPMXファイル 読み込み成功 %s", display_set_no, os.path.basename(rep_model_path)) - - # 元モデル ---------- - if len(camera_org_model_path) > 0: - logger.info("%s カメラ作成元モデルPMXファイル 読み込み開始", display_set_no) - - file_name, input_ext = os.path.splitext(os.path.basename(camera_org_model_path)) - if input_ext.lower() == ".pmx": - camera_org_model_reader = PmxReader(camera_org_model_path) - else: - raise SizingException("{0}.camera_org_model_path 読み込み失敗(拡張子不正): {1}".format(display_set_no, os.path.basename(camera_org_model_path))) - - camera_org_model = camera_org_model_reader.read_data() - - logger.info("%s カメラ作成元モデルPMXファイル 読み込み成功 %s", display_set_no, os.path.basename(camera_org_model_path)) - else: - # カメラ元モデルが未指定の場合、作成元モデルをそのまま流用 - camera_org_model = org_model - - detail_stance_flg = True if detail_stance_flg_val == 1 else False - twist_flg = True if twist_flg_val == 1 else False - - # 出力ファイルパス - output_vmd_path = MFileUtils.get_output_vmd_path(motion_path, rep_model_path, detail_stance_flg, twist_flg, arm_process_flg_avoidance, arm_process_flg_alignment, False, "", True) - - data_set = MOptionsDataSet( - motion, - org_model, - rep_model, - output_vmd_path, - detail_stance_flg, - twist_flg, - [], - camera_org_model, - camera_offset_y, - ["センターXZ補正", "上半身補正", "下半身補正", "足IK補正", "つま先IK補正", "つま先補正", "肩補正", "センターY補正"] - ) - - data_set_list.append(data_set) - - if len(args.camera_motion_path) != 0: - # カメラパス -------- - logger.info("調整対象カメラVMDファイル 読み込み開始") - - file_name, input_ext = os.path.splitext(os.path.basename(args.camera_motion_path)) - if input_ext.lower() == ".vmd": - camera_motion_reader = VmdReader(args.camera_motion_path) - else: - raise SizingException("camera_motion_path 読み込み失敗(拡張子不正): %s", os.path.basename(args.camera_motion_path)) - - camera_motion = camera_motion_reader.read_data() - camera_output_vmd_path = MFileUtils.get_output_camera_vmd_path(args.camera_motion_path, data_set_list[0].rep_model.path, "") - - logger.info("調整対象カメラVMD/VPDファイル 読み込み成功 %s", os.path.basename(args.camera_motion_path)) - else: - camera_motion = None - camera_output_vmd_path = None - - options = MOptions(\ - version_name=version_name, \ - logging_level=args.verbose, \ - data_set_list=data_set_list, \ - arm_options=arm_options, \ - camera_motion=camera_motion, \ - camera_output_vmd_path=camera_output_vmd_path, \ - monitor=sys.stdout, \ - is_file=True, \ - outout_datetime=logger.outout_datetime, \ - max_workers=1, \ - total_process=0, \ - now_process=0, \ - total_process_ctrl=None, \ - now_process_ctrl=None, \ - tree_process_dict={}) - - return options - except SizingException as se: - logger.error("サイジング処理が処理できないデータで終了しました。\n\n%s", se.message, decoration=MLogger.DECORATION_BOX) - except Exception as e: - logger.critical("サイジング処理が意図せぬエラーで終了しました。", e, decoration=MLogger.DECORATION_BOX) - - -class MOptionsDataSet(): - - def __init__(self, motion, org_model=None, rep_model=None, output_vmd_path=None, detail_stance_flg=0, twist_flg=0, morph_list=[], \ - camera_org_model=None, camera_offset_y=0, selected_stance_details=[]): - self.motion = motion - self.org_model = org_model - self.rep_model = rep_model - self.output_vmd_path = output_vmd_path - self.detail_stance_flg = detail_stance_flg - self.twist_flg = twist_flg - self.morph_list = morph_list - self.camera_org_model = camera_org_model - self.camera_offset_y = camera_offset_y - self.selected_stance_details = selected_stance_details - - self.org_motion = self.motion.copy() - self.test_params = None - self.full_arms = False - - # 本来の足IKの比率 - self.original_xz_ratio = 1 - self.original_y_ratio = 1 - - # 実際に計算に使う足IKの比率 - self.xz_ratio = 1 - self.y_ratio = 1 - - -class MArmProcessOptions(): - - def __init__(self, avoidance: bool, avoidance_target_list: list, alignment: bool, alignment_finger_flg: bool, alignment_floor_flg: bool, \ - alignment_distance_wrist: float, alignment_distance_finger: float, alignment_distance_floor: float, arm_check_skip_flg: bool): - self.avoidance = avoidance - self.avoidance_target_list = avoidance_target_list - self.alignment = alignment - self.alignment_finger_flg = alignment_finger_flg - self.alignment_floor_flg = alignment_floor_flg - self.alignment_distance_wrist = alignment_distance_wrist - self.alignment_distance_finger = alignment_distance_finger - self.alignment_distance_floor = alignment_distance_floor - self.arm_check_skip_flg = arm_check_skip_flg - - -class MCsvOptions(): - - def __init__(self, version_name, logging_level, motion): - self.version_name = version_name - self.logging_level = logging_level - self.motion = motion - - -class MVmdOptions(): - - def __init__(self, version_name, logging_level, bone_csv_path, morph_csv_path, camera_csv_path): - self.version_name = version_name - self.logging_level = logging_level - self.bone_csv_path = bone_csv_path - self.morph_csv_path = morph_csv_path - self.camera_csv_path = camera_csv_path - - -class MBlendOptions(): - - def __init__(self, version_name, logging_level, model, eye_list, eyebrow_list, lip_list, other_list, min_value, max_value, inc_value): - self.version_name = version_name - self.logging_level = logging_level - self.model = model - self.eye_list = eye_list - self.eyebrow_list = eyebrow_list - self.lip_list = lip_list - self.other_list = other_list - self.min_value = min_value - self.max_value = max_value - self.inc_value = inc_value - - -class MSmoothOptions(): - - def __init__(self, version_name, logging_level, max_workers, motion, model, output_path, loop_cnt, interpolation, monitor, is_file, outout_datetime): - self.version_name = version_name - self.logging_level = logging_level - self.motion = motion - self.model = model - self.output_path = output_path - self.loop_cnt = loop_cnt - self.interpolation = interpolation - self.monitor = monitor - self.is_file = is_file - self.outout_datetime = outout_datetime - self.max_workers = max_workers - - @classmethod - def parse(cls, version_name: str): - parser = argparse.ArgumentParser() - parser.add_argument('--motion_path', dest='motion_path', help='input vmd', type=str) - parser.add_argument('--model_path', dest='model_path', help='model_path', type=str) - parser.add_argument('--loop_cnt', dest='loop_cnt', help='loop_cnt', type=int) - parser.add_argument('--interpolation', dest='interpolation', help='interpolation', type=int) - parser.add_argument("--verbose", type=int, default=20) - - args = parser.parse_args() - - # ログディレクトリ作成 - os.makedirs("log", exist_ok=True) - - MLogger.initialize(level=args.verbose, is_file=True) - - try: - motion = VmdReader(args.motion_path).read_data() - model = PmxReader(args.model_path).read_data() - - # 出力ファイルパス - output_vmd_path = MFileUtils.get_output_smooth_vmd_path(motion.path, model.path, "", args.interpolation, args.loop_cnt, True) - - options = MSmoothOptions(\ - version_name=version_name, \ - logging_level=args.verbose, \ - motion=motion, \ - model=model, \ - output_path=output_vmd_path, \ - loop_cnt=args.loop_cnt, \ - interpolation=args.interpolation, \ - monitor=sys.stdout, \ - is_file=True, \ - outout_datetime=logger.outout_datetime, \ - max_workers=1) - - return options - except SizingException as se: - logger.error("スムージング処理が処理できないデータで終了しました。\n\n%s", se.message, decoration=MLogger.DECORATION_BOX) - except Exception as e: - logger.critical("スムージング処理が意図せぬエラーで終了しました。", e, decoration=MLogger.DECORATION_BOX) - - \ No newline at end of file diff --git a/src/module/MOptions.pyx b/src/module/MOptions.pyx new file mode 100644 index 0000000..0001994 --- /dev/null +++ b/src/module/MOptions.pyx @@ -0,0 +1,406 @@ +# -*- coding: utf-8 -*- +# +import os +import sys +import argparse + +from module.MMath import MRect, MVector2D, MVector3D, MVector4D, MQuaternion, MMatrix4x4 # noqa +from module.MParams import BoneLinks # noqa +from mmd.PmxData import PmxModel, Bone, Vertex, Material, Morph, DisplaySlot, RigidBody, Joint # noqa +from mmd.VmdData import VmdMotion, VmdBoneFrame, VmdCameraFrame, VmdInfoIk, VmdLightFrame, VmdMorphFrame, VmdShadowFrame, VmdShowIkFrame # noqa +from mmd.PmxReader import PmxReader +from mmd.VmdReader import VmdReader +from mmd.VpdReader import VpdReader +from utils import MFileUtils +from utils.MException import SizingException +from utils.MLogger import MLogger # noqa + +logger = MLogger(__name__) + + +cdef class MOptions(): + + def __init__(self, version_name, logging_level, max_workers, data_set_list, arm_options, \ + camera_motion, camera_output_vmd_path, monitor, is_file, outout_datetime, total_process, now_process, total_process_ctrl, now_process_ctrl, tree_process_dict): + self.version_name = version_name + self.logging_level = logging_level + self.max_workers = max_workers + self.data_set_list = data_set_list + self.arm_options = arm_options + self.camera_motion = camera_motion + self.camera_output_vmd_path = camera_output_vmd_path + self.monitor = monitor + self.is_file = is_file + self.outout_datetime = outout_datetime + self.total_process = total_process + self.now_process = now_process + self.total_process_ctrl = total_process_ctrl + self.now_process_ctrl = now_process_ctrl + self.tree_process_dict = tree_process_dict + + # 複数件のファイルセットの足IKの比率を再設定する + def calc_leg_ratio(self): + # まず一番小さいXZ比率と一番大きいXZ比率を取得する + min_xz_ratio = 99999999999 + max_xz_ratio = -99999999999 + for data_set_idx, data_set in enumerate(self.data_set_list): + if data_set.original_xz_ratio < min_xz_ratio: + min_xz_ratio = data_set.original_xz_ratio + + if data_set.original_xz_ratio > max_xz_ratio: + max_xz_ratio = data_set.original_xz_ratio + + # XZ比率の差(差分の1.2倍をリミットとする) + total_xz_ratio = min((min_xz_ratio + ((max_xz_ratio - min_xz_ratio) / 2)), 1.2) + logger.test("total_xz_ratio: %s", total_xz_ratio) + + logger.info("") + + log_txt = "足の長さの比率 ---------\n" + + for data_set_idx, data_set in enumerate(self.data_set_list): + if len(self.data_set_list) > 1: + # XZ比率は合計から導き出した比率 + data_set.xz_ratio = total_xz_ratio + data_set.y_ratio = data_set.original_y_ratio + else: + # セットが1件(一人モーションの場合はそのまま) + data_set.xz_ratio = data_set.original_xz_ratio + data_set.y_ratio = data_set.original_y_ratio + + log_txt = "{0}【No.{1}】 xz: {2}, y: {3} (元: xz: {4})\n".format(log_txt, (data_set_idx + 1), data_set.xz_ratio, data_set.y_ratio, data_set.original_xz_ratio) + + logger.info(log_txt) + + @classmethod + def parse(cls, version_name: str): + return c_parse(version_name) + +cdef c_parse(str version_name): + parser = argparse.ArgumentParser() + parser.add_argument("--motion_path", required=True, type=(lambda x: list(map(str, x.split(';'))))) + parser.add_argument("--org_model_path", required=True, type=(lambda x: list(map(str, x.split(';'))))) + parser.add_argument("--rep_model_path", required=True, type=(lambda x: list(map(str, x.split(';'))))) + parser.add_argument("--detail_stance_flg", required=True, type=(lambda x: list(map(int, x.split(';'))))) + parser.add_argument("--twist_flg", required=True, type=(lambda x: list(map(int, x.split(';'))))) + parser.add_argument("--arm_process_flg_avoidance", type=int, default=0) + parser.add_argument("--avoidance_target_list", default=[], type=(lambda x: list(map(str, x.split(';'))))) + parser.add_argument("--arm_process_flg_alignment", type=int, default=0) + parser.add_argument("--alignment_finger_flg", type=int, default=0) + parser.add_argument("--alignment_floor_flg", type=int, default=0) + parser.add_argument("--alignment_distance_wrist", type=float, default=1.7) + parser.add_argument("--alignment_distance_finger", type=float, default=1.4) + parser.add_argument("--alignment_distance_floor", type=float, default=1.8) + parser.add_argument("--arm_check_skip_flg", type=int, default=0) + parser.add_argument("--camera_motion_path", type=str, default="") + parser.add_argument("--camera_org_model_path", default=[], type=(lambda x: list(map(str, x.split(';'))))) + parser.add_argument("--camera_offset_y", default=[], type=(lambda x: list(map(str, x.split(';'))))) + parser.add_argument("--verbose", type=int, default=20) + + args = parser.parse_args() + + # ログディレクトリ作成 + os.makedirs("log", exist_ok=True) + + MLogger.initialize(level=args.verbose, is_file=True) + + try: + arm_process_flg_avoidance = True if args.arm_process_flg_avoidance == 1 else False + arm_process_flg_alignment = True if args.arm_process_flg_alignment == 1 else False + alignment_finger_flg = True if args.alignment_finger_flg == 1 else False + alignment_floor_flg = True if args.alignment_floor_flg == 1 else False + arm_check_skip_flg = True if args.arm_check_skip_flg == 1 else False + + arm_options = MArmProcessOptions( + arm_process_flg_avoidance, \ + {0: [(a.strip() if len(a.strip()) > 0 else "") for a in args.avoidance_target_list]}, \ + arm_process_flg_alignment, \ + alignment_finger_flg, \ + alignment_floor_flg, \ + args.alignment_distance_wrist, \ + args.alignment_distance_finger, \ + args.alignment_distance_floor, \ + arm_check_skip_flg + ) + + # 元モデルが未指定の場合、空で処理する + if not args.camera_org_model_path or (len(args.camera_org_model_path) == 1 and len(args.camera_org_model_path[0]) == 0): + args.camera_org_model_path = [] + for org_path in args.org_model_path: + args.camera_org_model_path.append("") + + # オフセットYが未指定の場合、0で処理する + if not args.camera_offset_y or (len(args.camera_offset_y) == 1 and len(args.camera_offset_y[0]) == 0): + args.camera_offset_y = [] + for org_path in args.org_model_path: + args.camera_offset_y.append(0) + + data_set_list = [] + for set_no, (motion_path, org_model_path, rep_model_path, detail_stance_flg_val, twist_flg_val, camera_org_model_path, camera_offset_y) in enumerate( \ + zip(args.motion_path, args.org_model_path, args.rep_model_path, args.detail_stance_flg, args.twist_flg, args.camera_org_model_path, \ + args.camera_offset_y)): # noqa + + display_set_no = "【No.{0}】".format(set_no + 1) + + # モーションパス -------- + logger.info("%s 調整対象モーションVMD/VPDファイル 読み込み開始", display_set_no) + + file_name, input_ext = os.path.splitext(os.path.basename(motion_path)) + if input_ext.lower() == ".vmd": + motion_reader = VmdReader(motion_path) + elif input_ext.lower() == ".vpd": + motion_reader = VpdReader(motion_path) + else: + raise SizingException("{0}.motion_path 読み込み失敗(拡張子不正): {1}".format(display_set_no, os.path.basename(motion_path))) + + motion = motion_reader.read_data() + + logger.info("%s 調整対象モーションVMD/VPDファイル 読み込み成功 %s", display_set_no, os.path.basename(motion_path)) + + # 元モデル ---------- + logger.info("%s モーション作成元モデルPMXファイル 読み込み開始", display_set_no) + + file_name, input_ext = os.path.splitext(os.path.basename(org_model_path)) + if input_ext.lower() == ".pmx": + org_model_reader = PmxReader(org_model_path) + else: + raise SizingException("{0}.org_model_path 読み込み失敗(拡張子不正): {1}".format(display_set_no, os.path.basename(org_model_path))) + + org_model = org_model_reader.read_data() + + logger.info("%s モーション作成元モデルPMXファイル 読み込み成功 %s", display_set_no, os.path.basename(org_model_path)) + + # 先モデル ---------- + logger.info("%s モーション変換先モデルPMXファイル 読み込み開始", display_set_no) + + file_name, input_ext = os.path.splitext(os.path.basename(rep_model_path)) + if input_ext.lower() == ".pmx": + rep_model_reader = PmxReader(rep_model_path) + else: + raise SizingException("{0}.rep_model_path 読み込み失敗(拡張子不正): {1}".format(display_set_no, os.path.basename(rep_model_path))) + + rep_model = rep_model_reader.read_data() + + logger.info("%s モーション変換先モデルPMXファイル 読み込み成功 %s", display_set_no, os.path.basename(rep_model_path)) + + # 元モデル ---------- + if len(camera_org_model_path) > 0: + logger.info("%s カメラ作成元モデルPMXファイル 読み込み開始", display_set_no) + + file_name, input_ext = os.path.splitext(os.path.basename(camera_org_model_path)) + if input_ext.lower() == ".pmx": + camera_org_model_reader = PmxReader(camera_org_model_path) + else: + raise SizingException("{0}.camera_org_model_path 読み込み失敗(拡張子不正): {1}".format(display_set_no, os.path.basename(camera_org_model_path))) + + camera_org_model = camera_org_model_reader.read_data() + + logger.info("%s カメラ作成元モデルPMXファイル 読み込み成功 %s", display_set_no, os.path.basename(camera_org_model_path)) + else: + # カメラ元モデルが未指定の場合、作成元モデルをそのまま流用 + camera_org_model = org_model + + detail_stance_flg = True if detail_stance_flg_val == 1 else False + twist_flg = True if twist_flg_val == 1 else False + + # 出力ファイルパス + output_vmd_path = MFileUtils.get_output_vmd_path(motion_path, rep_model_path, detail_stance_flg, twist_flg, arm_process_flg_avoidance, arm_process_flg_alignment, False, "", True) + + data_set = MOptionsDataSet( + motion, + org_model, + rep_model, + output_vmd_path, + detail_stance_flg, + twist_flg, + [], + camera_org_model, + camera_offset_y, + ["センターXZ補正", "上半身補正", "下半身補正", "足IK補正", "つま先IK補正", "つま先補正", "肩補正", "センターY補正"] + ) + + data_set_list.append(data_set) + + if len(args.camera_motion_path) != 0: + # カメラパス -------- + logger.info("調整対象カメラVMDファイル 読み込み開始") + + file_name, input_ext = os.path.splitext(os.path.basename(args.camera_motion_path)) + if input_ext.lower() == ".vmd": + camera_motion_reader = VmdReader(args.camera_motion_path) + else: + raise SizingException("camera_motion_path 読み込み失敗(拡張子不正): %s", os.path.basename(args.camera_motion_path)) + + camera_motion = camera_motion_reader.read_data() + camera_output_vmd_path = MFileUtils.get_output_camera_vmd_path(args.camera_motion_path, data_set_list[0].rep_model.path, "") + + logger.info("調整対象カメラVMD/VPDファイル 読み込み成功 %s", os.path.basename(args.camera_motion_path)) + else: + camera_motion = None + camera_output_vmd_path = None + + options = MOptions(\ + version_name=version_name, \ + logging_level=args.verbose, \ + data_set_list=data_set_list, \ + arm_options=arm_options, \ + camera_motion=camera_motion, \ + camera_output_vmd_path=camera_output_vmd_path, \ + monitor=sys.stdout, \ + is_file=True, \ + outout_datetime=logger.outout_datetime, \ + max_workers=1, \ + total_process=0, \ + now_process=0, \ + total_process_ctrl=None, \ + now_process_ctrl=None, \ + tree_process_dict={}) + + return options + except SizingException as se: + logger.error("サイジング処理が処理できないデータで終了しました。\n\n%s", se.message, decoration=MLogger.DECORATION_BOX) + except Exception as e: + logger.critical("サイジング処理が意図せぬエラーで終了しました。", e, decoration=MLogger.DECORATION_BOX) + + +cdef class MOptionsDataSet(): + + def __init__(self, motion, org_model=None, rep_model=None, output_vmd_path=None, detail_stance_flg=0, twist_flg=0, morph_list=[], \ + camera_org_model=None, camera_offset_y=0, selected_stance_details=[]): + self.motion = motion + self.org_model = org_model + self.rep_model = rep_model + self.output_vmd_path = output_vmd_path + self.detail_stance_flg = detail_stance_flg + self.twist_flg = twist_flg + self.morph_list = morph_list + self.camera_org_model = camera_org_model + self.camera_offset_y = camera_offset_y + self.selected_stance_details = selected_stance_details + + self.org_motion = self.motion.copy() + self.test_params = None + self.full_arms = False + + # 本来の足IKの比率 + self.original_xz_ratio = 1 + self.original_y_ratio = 1 + + # 実際に計算に使う足IKの比率 + self.xz_ratio = 1 + self.y_ratio = 1 + + +cdef class MArmProcessOptions(): + + def __init__(self, avoidance: bool, avoidance_target_list: dict, alignment: bool, alignment_finger_flg: bool, alignment_floor_flg: bool, \ + alignment_distance_wrist: float, alignment_distance_finger: float, alignment_distance_floor: float, arm_check_skip_flg: bool): + self.avoidance = avoidance + self.avoidance_target_list = avoidance_target_list + self.alignment = alignment + self.alignment_finger_flg = alignment_finger_flg + self.alignment_floor_flg = alignment_floor_flg + self.alignment_distance_wrist = alignment_distance_wrist + self.alignment_distance_finger = alignment_distance_finger + self.alignment_distance_floor = alignment_distance_floor + self.arm_check_skip_flg = arm_check_skip_flg + + +class MCsvOptions(): + + def __init__(self, version_name, logging_level, motion): + self.version_name = version_name + self.logging_level = logging_level + self.motion = motion + + +class MVmdOptions(): + + def __init__(self, version_name, logging_level, bone_csv_path, morph_csv_path, camera_csv_path): + self.version_name = version_name + self.logging_level = logging_level + self.bone_csv_path = bone_csv_path + self.morph_csv_path = morph_csv_path + self.camera_csv_path = camera_csv_path + + +class MBlendOptions(): + + def __init__(self, version_name, logging_level, model, eye_list, eyebrow_list, lip_list, other_list, min_value, max_value, inc_value): + self.version_name = version_name + self.logging_level = logging_level + self.model = model + self.eye_list = eye_list + self.eyebrow_list = eyebrow_list + self.lip_list = lip_list + self.other_list = other_list + self.min_value = min_value + self.max_value = max_value + self.inc_value = inc_value + + +cdef class MSmoothOptions(): + + def __init__(self, version_name, logging_level, max_workers, motion, model, output_path, loop_cnt, interpolation, bone_list, monitor, is_file, outout_datetime): + self.version_name = version_name + self.logging_level = logging_level + self.motion = motion + self.model = model + self.output_path = output_path + self.loop_cnt = loop_cnt + self.interpolation = interpolation + self.bone_list = bone_list + self.monitor = monitor + self.is_file = is_file + self.outout_datetime = outout_datetime + self.max_workers = max_workers + + @classmethod + def parse(cls, version_name: str): + return c_smooth_parse(version_name) + + +cdef c_smooth_parse(str version_name): + parser = argparse.ArgumentParser() + parser.add_argument('--motion_path', dest='motion_path', help='input vmd', type=str) + parser.add_argument('--model_path', dest='model_path', help='model_path', type=str) + parser.add_argument('--loop_cnt', dest='loop_cnt', help='loop_cnt', type=int) + parser.add_argument('--interpolation', dest='interpolation', help='interpolation', type=int) + parser.add_argument("--bone_list", default=[], type=(lambda x: list(map(str, x.split(';'))))) + parser.add_argument("--verbose", type=int, default=20) + + args = parser.parse_args() + + # ログディレクトリ作成 + os.makedirs("log", exist_ok=True) + + MLogger.initialize(level=args.verbose, is_file=True) + + try: + motion = VmdReader(args.motion_path).read_data() + model = PmxReader(args.model_path).read_data() + + # 出力ファイルパス + output_vmd_path = MFileUtils.get_output_smooth_vmd_path(motion.path, model.path, "", args.interpolation, args.loop_cnt, True) + + options = MSmoothOptions(\ + version_name=version_name, \ + logging_level=args.verbose, \ + motion=motion, \ + model=model, \ + output_path=output_vmd_path, \ + loop_cnt=args.loop_cnt, \ + interpolation=args.interpolation, \ + bone_list=args.bone_list, \ + monitor=sys.stdout, \ + is_file=True, \ + outout_datetime=logger.outout_datetime, \ + max_workers=1) + + return options + except SizingException as se: + logger.error("スムージング処理が処理できないデータで終了しました。\n\n%s", se.message, decoration=MLogger.DECORATION_BOX) + except Exception as e: + logger.critical("スムージング処理が意図せぬエラーで終了しました。", e, decoration=MLogger.DECORATION_BOX) + + diff --git a/src/module/MParams.pxd b/src/module/MParams.pxd new file mode 100644 index 0000000..dc3005c --- /dev/null +++ b/src/module/MParams.pxd @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# + +cdef class BoneLinks: + cdef dict __links + diff --git a/src/module/MParams.py b/src/module/MParams.pyx similarity index 97% rename from src/module/MParams.py rename to src/module/MParams.pyx index 2690401..4a0f57f 100644 --- a/src/module/MParams.py +++ b/src/module/MParams.pyx @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- # -from collections import OrderedDict -class BoneLinks(): +cdef class BoneLinks: + def __init__(self): - self.__links = OrderedDict() + self.__links = {} def get(self, bone_name: str, offset=0): if bone_name not in self.__links: diff --git a/src/module/StdoutQueue.py b/src/module/StdoutQueue.py deleted file mode 100644 index 4cea389..0000000 --- a/src/module/StdoutQueue.py +++ /dev/null @@ -1,38 +0,0 @@ -# -*- coding: utf-8 -*- -# - -import sys -import multiprocessing - - -class StdoutQueue(): - - queue = None - - # 初期化 - def __init__(self, *args, **kwargs): - m = multiprocessing.Manager() - self.queue = m.Queue(*args, **kwargs) - - def get(self, *args, **kwargs): - try: - return self.queue.get(*args, **kwargs) - except Exception as e: - raise e - - def put(self, obj): - try: - self.queue.put(obj) - except Exception: - return None - - # printでの出力 - def write(self, msg): - try: - self.queue.put(msg) - except Exception: - pass - - def flush(self): - sys.__stdout__.flush() - diff --git a/src/setup.bat b/src/setup.bat index 28749f3..88b4c05 100644 --- a/src/setup.bat +++ b/src/setup.bat @@ -4,10 +4,10 @@ cls cd /d %~dp0 rem -- svt@Cp -rem kernprof -l setup.py build_ext --inplace +kernprof -l setup.py build_ext --inplace rem -- ʏp -python setup.py build_ext --inplace +rem python setup.py build_ext --inplace cd .. diff --git a/src/setup_ext.py b/src/setup_ext.py index 2c7f487..47c83ae 100644 --- a/src/setup_ext.py +++ b/src/setup_ext.py @@ -8,8 +8,8 @@ def get_ext(): ext = [] - sources = ["module\\MMath.pyx", "module\\MParams.py", \ - "utils\\MBezierUtils.pyx", \ + sources = ["module\\MMath.pyx", "module\\MOptions.pyx", "module\\MParams.pyx", \ + "utils\\MLogger.py", "utils\\MBezierUtils.pyx", "utils\\MServiceUtils.pyx", \ "mmd\\VmdData.pyx", "mmd\\VmdReader.py", "mmd\\PmxData.pyx", "mmd\\PmxReader.py"] # for path in glob.glob("*/**/*.pyx", recursive=True): # if os.path.isfile(path): diff --git a/src/utils/MLogger.py b/src/utils/MLogger.py index 3b5cf0f..6a5961a 100644 --- a/src/utils/MLogger.py +++ b/src/utils/MLogger.py @@ -4,6 +4,9 @@ import logging import traceback import threading +import sys + +import cython from utils.MException import MKilledException @@ -146,7 +149,7 @@ def print_logger(self, msg, *args, **kwargs): extra_args["module_name"] = self.module_name # ログレコード生成 - if args and isinstance(args[0], Exception): + if args and isinstance(args[0], Exception) or (args and len(args) > 1 and isinstance(args[0], Exception)): log_record = self.logger.makeRecord('name', target_level, "(unknown file)", 0, "{0}\n\n{1}".format(msg, traceback.format_exc()), None, None, self.module_name) else: log_record = self.logger.makeRecord('name', target_level, "(unknown file)", 0, msg, args, None, self.module_name) @@ -181,7 +184,7 @@ def print_logger(self, msg, *args, **kwargs): self.logger.handle(log_record) else: # サイジングスレッドは、printとloggerで分けて出力 - print(output_msg) + print_message(output_msg, target_level) self.logger.handle(log_record) except Exception as e: raise e @@ -241,3 +244,10 @@ def initialize(cls, level=logging.INFO, is_file=False): cls.total_level = level cls.is_file = is_file cls.outout_datetime = "{0:%Y%m%d_%H%M%S}".format(datetime.now()) + + +@cython.ccall +def print_message(msg: str, target_level: int): + sys.stdout.write("\n" + msg, (target_level < MLogger.INFO)) + + diff --git a/src/utils/MServiceUtils.pxd b/src/utils/MServiceUtils.pxd new file mode 100644 index 0000000..891750a --- /dev/null +++ b/src/utils/MServiceUtils.pxd @@ -0,0 +1,30 @@ +import math +import numpy as np +cimport numpy as np + +from mmd.PmxData cimport PmxModel, Bone +from mmd.VmdData cimport VmdMotion, VmdBoneFrame +from module.MParams cimport BoneLinks # noqa +from module.MMath cimport MRect, MVector2D, MVector3D, MVector4D, MQuaternion, MMatrix4x4 # noqa + +cdef c_calc_IK(PmxModel model, BoneLinks links, VmdMotion motion, int fno, MVector3D target_pos, BoneLinks ik_links, int max_count) + +cdef tuple c_separate_local_qq(int fno, str bone_name, MQuaternion qq, MVector3D global_x_axis) + +cdef tuple c_calc_global_pos(PmxModel model, BoneLinks links, VmdMotion motion, int fno, BoneLinks limit_links, bint return_matrix, bint is_local_x) + +cpdef dict calc_global_pos_by_direction(MQuaternion direction_qq, dict target_pos_3ds_dic) + +cdef list c_calc_relative_position(PmxModel model, BoneLinks links, VmdMotion motion, int fno, BoneLinks limit_links) + +cdef list c_calc_relative_rotation(PmxModel model, BoneLinks links, VmdMotion motion, int fno, BoneLinks limit_links) + +cpdef MQuaternion deform_rotation(PmxModel model, VmdMotion motion, VmdBoneFrame bf) + +cpdef MQuaternion deform_fix_rotation(str bone_name, MVector3D fixed_axis, MQuaternion rot) + +cdef MQuaternion c_calc_direction_qq(PmxModel model, BoneLinks links, VmdMotion motion, int fno, BoneLinks limit_links) + + + + diff --git a/src/utils/MServiceUtils.py b/src/utils/MServiceUtils.pyx similarity index 64% rename from src/utils/MServiceUtils.py rename to src/utils/MServiceUtils.pyx index d5175b4..4faacbb 100644 --- a/src/utils/MServiceUtils.py +++ b/src/utils/MServiceUtils.pyx @@ -1,15 +1,16 @@ # -*- coding: utf-8 -*- # -import copy import numpy as np # noqa import math # noqa -from collections import OrderedDict +import numpy as np +cimport numpy as np +from libc.math cimport sin, cos, acos, atan2, asin, pi, sqrt -from mmd.PmxData import PmxModel, Vertex, Material, Bone, Morph, DisplaySlot, RigidBody, Joint # noqa -from mmd.VmdData import VmdMotion, VmdBoneFrame, VmdCameraFrame, VmdInfoIk, VmdLightFrame, VmdMorphFrame, VmdShadowFrame, VmdShowIkFrame # noqa -from module.MMath import MRect, MVector2D, MVector3D, MVector4D, MQuaternion, MMatrix4x4 # noqa -from module.MOptions import MOptions, MOptionsDataSet # noqa from module.MParams import BoneLinks # noqa +from module.MMath import MRect, MVector2D, MVector3D, MVector4D, MQuaternion, MMatrix4x4 # noqa +from mmd.PmxData import PmxModel, Bone, Vertex, Material, Morph, DisplaySlot, RigidBody, Joint # noqa +from mmd.VmdData import VmdMotion, VmdBoneFrame, VmdCameraFrame, VmdInfoIk, VmdLightFrame, VmdMorphFrame, VmdShadowFrame, VmdShowIkFrame # noqa +from module.MOptions import MOptionsDataSet # noqa from utils import MBezierUtils # noqa from utils.MLogger import MLogger # noqa @@ -20,22 +21,52 @@ # target_pos: IKリンクの目的位置 # ik_links: IKリンク def calc_IK(model: PmxModel, links: BoneLinks, motion: VmdMotion, fno: int, target_pos: MVector3D, ik_links: BoneLinks, max_count=10): - for bone_name in list(ik_links.all().keys())[1:]: + c_calc_IK(model, links, motion, fno, target_pos, ik_links, max_count) + +cdef c_calc_IK(PmxModel model, BoneLinks links, VmdMotion motion, int fno, MVector3D target_pos, BoneLinks ik_links, int max_count): + cdef list bone_name_list = list(ik_links.all().keys())[1:] + cdef str bone_name + cdef VmdBoneFrame bf + + for bone_name in bone_name_list: # bfをモーションに登録 - bf = motion.calc_bf(bone_name, fno) + bf = motion.c_calc_bf(bone_name, fno, is_key=False, is_read=False, is_reset_interpolation=False) motion.regist_bf(bf, bone_name, fno) + cdef MVector3D local_effector_pos + cdef MVector3D local_target_pos + local_effector_pos = MVector3D() local_target_pos = MVector3D() + cdef int cnt + cdef int ik_idx + cdef str joint_name + cdef Bone ik_bone + cdef dict global_3ds_dic + cdef dict total_mats + cdef MVector3D global_effector_pos + cdef MMatrix4x4 joint_mat + cdef MMatrix4x4 inv_coord + cdef MVector3D basis2_effector + cdef MVector3D basis2_target + cdef double rotation_dot + cdef double rotation_radian + cdef MVector3D rotation_axis + cdef double rotation_degree + cdef MQuaternion correct_qq + cdef MQuaternion new_ik_qq + cdef MQuaternion x_qq, y_qq, z_qq, yz_qq + cdef double euler_x, euler_y, euler_z + for cnt in range(max_count): # 規定回数ループ - for ik_idx, joint_name in enumerate(list(ik_links.all().keys())[1:]): + for ik_idx, joint_name in enumerate(bone_name_list): # 処理対象IKボーン ik_bone = ik_links.get(joint_name) # 現在のボーングローバル位置と行列を取得 - global_3ds_dic, total_mats = calc_global_pos(model, links, motion, fno, return_matrix=True) + global_3ds_dic, total_mats = c_calc_global_pos(model, links, motion, fno, limit_links=None, return_matrix=True, is_local_x=False) # エフェクタ(末端) global_effector_pos = global_3ds_dic[ik_links.first_name()] @@ -59,7 +90,7 @@ def calc_IK(model: PmxModel, links: BoneLinks, motion: VmdMotion, fno: int, targ # 回転角 rotation_dot = MVector3D.dotProduct(basis2_effector, basis2_target) # 回転角度 - rotation_radian = math.acos(max(-1, min(1, rotation_dot))) + rotation_radian = acos(max(-1, min(1, rotation_dot))) if abs(rotation_radian) > 0.0001: # 一定角度以上の場合 @@ -73,12 +104,12 @@ def calc_IK(model: PmxModel, links: BoneLinks, motion: VmdMotion, fno: int, targ correct_qq = MQuaternion.fromAxisAndAngle(rotation_axis, min(rotation_degree, ik_bone.degree_limit)) # ジョイントに補正をかける - bf = motion.calc_bf(joint_name, fno) + bf = motion.c_calc_bf(joint_name, fno, is_key=False, is_read=False, is_reset_interpolation=False) new_ik_qq = correct_qq * bf.rotation # IK軸制限がある場合、上限下限をチェック if ik_bone.ik_limit_min != MVector3D() and ik_bone.ik_limit_max != MVector3D(): - x_qq, y_qq, z_qq, yz_qq = separate_local_qq(fno, bone_name, new_ik_qq, model.get_local_x_axis(ik_bone.name)) + x_qq, y_qq, z_qq, yz_qq = separate_local_qq(fno, ik_bone.name, new_ik_qq, model.get_local_x_axis(ik_bone.name)) logger.test("new_ik_qq: %s, x_qq: %s, y_qq: %s, z_qq: %s", new_ik_qq.toEulerAngles(), x_qq.toEulerAngles(), y_qq.toEulerAngles(), z_qq.toEulerAngles()) logger.test("new_ik_qq: %s, x_qq: %s, y_qq: %s, z_qq: %s", new_ik_qq.toDegree(), x_qq.toDegree(), y_qq.toDegree(), z_qq.toDegree()) @@ -102,136 +133,184 @@ def calc_IK(model: PmxModel, links: BoneLinks, motion: VmdMotion, fno: int, targ # クォータニオンをローカル軸の回転量に分離 def separate_local_qq(fno: int, bone_name: str, qq: MQuaternion, global_x_axis: MVector3D): + return_tuple = c_separate_local_qq(fno, bone_name, qq, global_x_axis) + return return_tuple[0], return_tuple[1], return_tuple[2], return_tuple[3] + +cdef tuple c_separate_local_qq(int fno, str bone_name, MQuaternion qq, MVector3D global_x_axis): # ローカル座標系(ボーンベクトルが(1,0,0)になる空間)の向き - local_axis = MVector3D(1, 0, 0) + cdef MVector3D local_axis = MVector3D(1, 0, 0) # グローバル座標系(Aスタンス)からローカル座標系(ボーンベクトルが(1,0,0)になる空間)への変換 - global2local_qq = MQuaternion.rotationTo(global_x_axis, local_axis) - local2global_qq = MQuaternion.rotationTo(local_axis, global_x_axis) + cdef MQuaternion global2local_qq = MQuaternion.rotationTo(global_x_axis, local_axis) + cdef MQuaternion local2global_qq = MQuaternion.rotationTo(local_axis, global_x_axis) # X成分を抽出する ------------ - mat_x1 = MMatrix4x4() + cdef MMatrix4x4 mat_x1 = MMatrix4x4() mat_x1.setToIdentity() # 初期化 mat_x1.rotate(qq) # 入力qq mat_x1.translate(global_x_axis) # グローバル軸方向に伸ばす - mat_x1_vec = mat_x1 * MVector3D() + cdef MVector3D mat_x1_vec = mat_x1 * MVector3D() # YZの回転量(自身のねじれを無視する) - yz_qq = MQuaternion.rotationTo(global_x_axis, mat_x1_vec) + cdef MQuaternion yz_qq = MQuaternion.rotationTo(global_x_axis, mat_x1_vec) # 除去されたX成分を求める - mat_x2 = MMatrix4x4() + cdef MMatrix4x4 mat_x2 = MMatrix4x4() mat_x2.setToIdentity() # 初期化 mat_x2.rotate(qq) # 元々の回転量 - mat_x3 = MMatrix4x4() + cdef MMatrix4x4 mat_x3 = MMatrix4x4() mat_x3.setToIdentity() # 初期化 mat_x3.rotate(yz_qq) # YZの回転量 - x_qq = (mat_x2 * mat_x3.inverted()).toQuaternion() + cdef MQuaternion x_qq = (mat_x2 * mat_x3.inverted()).toQuaternion() # YZ回転からZ成分を抽出する -------------- - mat_z1 = MMatrix4x4() + cdef MMatrix4x4 mat_z1 = MMatrix4x4() mat_z1.setToIdentity() # 初期化 mat_z1.rotate(yz_qq) # YZの回転量 mat_z1.rotate(global2local_qq) # グローバル軸の回転量からローカルの回転量に変換 mat_z1.translate(local_axis) # ローカル軸方向に伸ばす - mat_z1_vec = mat_z1 * MVector3D() + cdef MVector3D mat_z1_vec = mat_z1 * MVector3D() mat_z1_vec.setZ(0) # Z方向の移動量を潰す # ローカル軸からZを潰した移動への回転量 - local_z_qq = MQuaternion.rotationTo(local_axis, mat_z1_vec) + cdef MQuaternion local_z_qq = MQuaternion.rotationTo(local_axis, mat_z1_vec) # ボーンローカル座標系の回転をグローバル座標系の回転に戻す - mat_z2 = MMatrix4x4() + cdef MMatrix4x4 mat_z2 = MMatrix4x4() mat_z2.setToIdentity() # 初期化 mat_z2.rotate(local_z_qq) # ローカル軸上のZ回転 mat_z2.rotate(local2global_qq) # ローカル軸上からグローバル軸上に変換 - z_qq = mat_z2.toQuaternion() + cdef MQuaternion z_qq = mat_z2.toQuaternion() # YZ回転からY成分だけ取り出す ----------- - mat_y1 = MMatrix4x4() + cdef MMatrix4x4 mat_y1 = MMatrix4x4() mat_y1.setToIdentity() # 初期化 mat_y1.rotate(yz_qq) # グローバルYZの回転量 - mat_y2 = MMatrix4x4() + cdef MMatrix4x4 mat_y2 = MMatrix4x4() mat_y2.setToIdentity() # 初期化 mat_y2.rotate(z_qq) # グローバルZの回転量 - mat_y2_qq = (mat_y1 * mat_y2.inverted()).toQuaternion() + cdef MQuaternion mat_y2_qq = (mat_y1 * mat_y2.inverted()).toQuaternion() # X成分の捻れが混入したので、XY回転からYZ回転を取り出すことでXキャンセルをかける。 - mat_y3 = MMatrix4x4() + cdef MMatrix4x4 mat_y3 = MMatrix4x4() mat_y3.setToIdentity() mat_y3.rotate(mat_y2_qq) mat_y3.translate(global_x_axis) - mat_y3_vec = mat_y3 * MVector3D() + cdef MVector3D mat_y3_vec = mat_y3 * MVector3D() + + cdef MQuaternion y_qq = MQuaternion.rotationTo(global_x_axis, mat_y3_vec) - y_qq = MQuaternion.rotationTo(global_x_axis, mat_y3_vec) + # Xを再度求める ------------- - return x_qq, y_qq, z_qq, yz_qq + cdef MMatrix4x4 mat_x4 = MMatrix4x4() + mat_x4.setToIdentity() + mat_x4.rotate(qq) + cdef MMatrix4x4 mat_x5 = MMatrix4x4() + mat_x5.setToIdentity() + mat_x5.rotate(y_qq) + + cdef MMatrix4x4 mat_x6 = MMatrix4x4() + mat_x6.setToIdentity() + mat_x6.rotate(z_qq) + + x_qq = (mat_x5.inverted() * mat_x4 * mat_x6.inverted()).toQuaternion() + + return (x_qq, y_qq, z_qq, yz_qq) # 正面向きの情報を含むグローバル位置 def calc_front_global_pos(model: PmxModel, links: BoneLinks, motion: VmdMotion, fno: int, limit_links=None, direction_limit_links=None): + return_tuple = c_calc_front_global_pos(model, links, motion, fno, limit_links, direction_limit_links) + return return_tuple[0], return_tuple[1], return_tuple[2] +cdef tuple c_calc_front_global_pos(PmxModel model, BoneLinks links, VmdMotion motion, int fno, BoneLinks limit_links, BoneLinks direction_limit_links): # グローバル位置 - global_3ds = org_center_global_3ds = calc_global_pos(model, links, motion, fno, limit_links) + cdef dict global_3ds, org_center_global_3ds, total_mats + + (global_3ds, total_mats) = c_calc_global_pos(model, links, motion, fno, limit_links, False, False) + org_center_global_3ds = global_3ds # 指定ボーンまでの向いている回転量(回転のみの制限がかかっている場合、それを優先) - direction_qq = calc_direction_qq(model, links, motion, fno, (limit_links if not direction_limit_links else direction_limit_links)) + cdef MQuaternion direction_qq = c_calc_direction_qq(model, links, motion, fno, (limit_links if not direction_limit_links else direction_limit_links)) # 正面向きのグローバル位置 - front_global_3ds = calc_global_pos_by_direction(direction_qq.inverted(), org_center_global_3ds) + cdef dict front_global_3ds = calc_global_pos_by_direction(direction_qq.inverted(), org_center_global_3ds) - return global_3ds, front_global_3ds, direction_qq + return (global_3ds, front_global_3ds, direction_qq) # グローバル位置算出 def calc_global_pos(model: PmxModel, links: BoneLinks, motion: VmdMotion, fno: int, limit_links=None, return_matrix=False, is_local_x=False): - trans_vs = calc_relative_position(model, links, motion, fno, limit_links) - add_qs = calc_relative_rotation(model, links, motion, fno, limit_links) + # cfun = profile(c_calc_global_pos) + # return_tuple = cfun(model, links, motion, fno, limit_links, return_matrix, is_local_x) + return_tuple = c_calc_global_pos(model, links, motion, fno, limit_links, return_matrix, is_local_x) + if not return_matrix: + return return_tuple[0] + else: + # 行列も返す場合 + return return_tuple[0], return_tuple[1] + +cdef tuple c_calc_global_pos(PmxModel model, BoneLinks links, VmdMotion motion, int fno, BoneLinks limit_links, bint return_matrix, bint is_local_x): + # pfun = profile(c_calc_relative_position) + # cdef list trans_vs = pfun(model, links, motion, fno, limit_links) + cdef list trans_vs = c_calc_relative_position(model, links, motion, fno, limit_links) + cdef list add_qs = c_calc_relative_rotation(model, links, motion, fno, limit_links) # 行列 - matrixs = [MMatrix4x4() for i in range(links.size())] + cdef list matrixs = [MMatrix4x4() for i in range(links.size())] + cdef int n + cdef str lname + cdef MVector3D v + cdef MQuaternion q + cdef MMatrix4x4 mm for n, (lname, v, q) in enumerate(zip(links.all().keys(), trans_vs, add_qs)): # 行列を生成 - matrixs[n] = MMatrix4x4() + mm = MMatrix4x4() # 初期化 - matrixs[n].setToIdentity() + mm.setToIdentity() # 移動 - matrixs[n].translate(v) + mm.translate(v) # 回転 - matrixs[n].rotate(q) + mm.rotate(q) + # 設定 + matrixs[n] = mm + + cdef dict total_mats = {} + cdef dict global_3ds_dic = {} - total_mats = {} - global_3ds_dic = OrderedDict() + cdef MMatrix4x4 local_x_matrix + cdef MVector3D local_axis + cdef MQuaternion local_axis_qq for n, (lname, v) in enumerate(zip(links.all().keys(), trans_vs)): if n == 0: - total_mats[lname] = MMatrix4x4() - total_mats[lname].setToIdentity() + mm = MMatrix4x4() + mm.setToIdentity() for m in range(n): # 最後のひとつ手前までループ if m == 0: # 0番目の位置を初期値とする - total_mats[lname] = matrixs[0].copy() + mm = matrixs[0].copy() else: # 自分より前の行列結果を掛け算する - total_mats[lname] *= matrixs[m].copy() + mm *= matrixs[m] # 自分は、位置だけ掛ける - global_3ds_dic[lname] = total_mats[lname] * v - + global_3ds_dic[lname] = mm * v + # 最後の行列をかけ算する - total_mats[lname] *= matrixs[n].copy() - + total_mats[lname] = mm * matrixs[n] + # ローカル軸の向きを調整する if n > 0 and is_local_x: # ボーン自身にローカル軸が設定されているか @@ -254,16 +333,15 @@ def calc_global_pos(model: PmxModel, links: BoneLinks, motion: VmdMotion, fno: i total_mats[lname] *= local_x_matrix - if return_matrix: - # 行列も返す場合 - return global_3ds_dic, total_mats - - return global_3ds_dic + return (global_3ds_dic, total_mats) # 指定された方向に向いた場合の位置情報を返す -def calc_global_pos_by_direction(direction_qq: MQuaternion, target_pos_3ds_dic: OrderedDict): - direction_pos_dic = OrderedDict() +cpdef dict calc_global_pos_by_direction(MQuaternion direction_qq, dict target_pos_3ds_dic): + cdef dict direction_pos_dic = {} + cdef str bone_name + cdef MVector3D target_pos + cdef MMatrix4x4 mat for bone_name, target_pos in target_pos_3ds_dic.items(): # # その地点の回転後の位置 @@ -286,14 +364,21 @@ def calc_global_pos_by_direction(direction_qq: MQuaternion, target_pos_3ds_dic: # 各ボーンの相対位置情報 def calc_relative_position(model: PmxModel, links: BoneLinks, motion: VmdMotion, fno: int, limit_links=None): - trans_vs = [] + return c_calc_relative_position(model, links, motion, fno, limit_links) + +cdef list c_calc_relative_position(PmxModel model, BoneLinks links, VmdMotion motion, int fno, BoneLinks limit_links): + cdef list trans_vs = [] + cdef int link_idx + cdef str link_bone_name + cdef Bone link_bone + cdef VmdBoneFrame fill_bf for link_idx, link_bone_name in enumerate(links.all()): link_bone = links.get(link_bone_name) if not limit_links or (limit_links and limit_links.get(link_bone_name)): # 上限リンクがある倍、ボーンが存在している場合のみ、モーション内のキー情報を取得 - fill_bf = motion.calc_bf(link_bone.name, fno) + fill_bf = motion.c_calc_bf(link_bone.name, fno, is_key=False, is_read=False, is_reset_interpolation=False) else: # 上限リンクでボーンがない場合、ボーンは初期値 fill_bf = VmdBoneFrame(fno=fno) @@ -312,14 +397,22 @@ def calc_relative_position(model: PmxModel, links: BoneLinks, motion: VmdMotion, # 各ボーンの相対回転情報 def calc_relative_rotation(model: PmxModel, links: BoneLinks, motion: VmdMotion, fno: int, limit_links=None): - add_qs = [] + return c_calc_relative_rotation(model, links, motion, fno, limit_links) + +cdef list c_calc_relative_rotation(PmxModel model, BoneLinks links, VmdMotion motion, int fno, BoneLinks limit_links): + cdef list add_qs = [] + cdef int link_idx + cdef str link_bone_name + cdef Bone link_bone + cdef VmdBoneFrame fill_bf + cdef MQuaternion rot for link_idx, link_bone_name in enumerate(links.all()): link_bone = links.get(link_bone_name) if not limit_links or (limit_links and limit_links.get(link_bone_name)): # 上限リンクがある場合、ボーンが存在している場合のみ、モーション内のキー情報を取得 - fill_bf = motion.calc_bf(link_bone.name, fno) + fill_bf = motion.c_calc_bf(link_bone.name, fno, is_key=False, is_read=False, is_reset_interpolation=False) else: # 上限リンクでボーンがない場合、ボーンは初期値 fill_bf = VmdBoneFrame(fno=fno) @@ -334,36 +427,20 @@ def calc_relative_rotation(model: PmxModel, links: BoneLinks, motion: VmdMotion, # 指定ボーンの実際の回転情報 -def deform_rotation(model: PmxModel, motion: VmdMotion, bf: VmdBoneFrame): +cpdef MQuaternion deform_rotation(PmxModel model, VmdMotion motion, VmdBoneFrame bf): if bf.name not in model.bones: return MQuaternion() - bone = model.bones[bf.name] - rot = bf.rotation.normalized().copy() + cdef Bone bone = model.bones[bf.name] + cdef MQuaternion rot = bf.rotation.normalized().copy() + + rot = deform_fix_rotation(bf.name, bone.fixed_axis, rot) + + cdef Bone effect_parent_bone + cdef Bone effect_bone + cdef int cnt + cdef VmdBoneFrame effect_bf - if bone.fixed_axis != MVector3D(): - # 回転角度を求める - if rot != MQuaternion(): - # 回転補正 - if "右" in bone.name and rot.x() > 0 and bone.fixed_axis.x() <= 0: - rot.setX(rot.x() * -1) - rot.setScalar(rot.scalar() * -1) - elif "左" in bone.name and rot.x() < 0 and bone.fixed_axis.x() >= 0: - rot.setX(rot.x() * -1) - rot.setScalar(rot.scalar() * -1) - # 回転補正(コロン式ミクさん等軸反転パターン) - elif "右" in bone.name and rot.x() < 0 and bone.fixed_axis.x() > 0: - rot.setX(rot.x() * -1) - rot.setScalar(rot.scalar() * -1) - elif "左" in bone.name and rot.x() > 0 and bone.fixed_axis.x() < 0: - rot.setX(rot.x() * -1) - rot.setScalar(rot.scalar() * -1) - - rot.normalize() - - # 軸固定の場合、回転を制限する - rot = MQuaternion.fromAxisAndAngle(bone.fixed_axis, rot.toDegree()) - if bone.getExternalRotationFlag() and bone.effect_index in model.bone_indexes: effect_parent_bone = bone @@ -372,7 +449,7 @@ def deform_rotation(model: PmxModel, motion: VmdMotion, bf: VmdBoneFrame): while cnt < 100: # 付与親が取得できたら、該当する付与親の回転を取得する - effect_bf = motion.calc_bf(effect_bone.name, bf.fno) + effect_bf = motion.c_calc_bf(effect_bone.name, bf.fno, is_key=False, is_read=False, is_reset_interpolation=False) # 自身の回転量に付与親の回転量を付与率を加味して付与する if effect_parent_bone.effect_factor < 0: @@ -394,11 +471,42 @@ def deform_rotation(model: PmxModel, motion: VmdMotion, bf: VmdBoneFrame): return rot +# 軸制限回転を求め直す +cpdef MQuaternion deform_fix_rotation(str bone_name, MVector3D fixed_axis, MQuaternion rot): + if fixed_axis != MVector3D(): + # 回転角度を求める + if rot != MQuaternion(): + # 回転補正 + if "右" in bone_name and rot.x() > 0 and fixed_axis.x() <= 0: + rot.setX(rot.x() * -1) + rot.setScalar(rot.scalar() * -1) + elif "左" in bone_name and rot.x() < 0 and fixed_axis.x() >= 0: + rot.setX(rot.x() * -1) + rot.setScalar(rot.scalar() * -1) + # 回転補正(コロン式ミクさん等軸反転パターン) + elif "右" in bone_name and rot.x() < 0 and fixed_axis.x() > 0: + rot.setX(rot.x() * -1) + rot.setScalar(rot.scalar() * -1) + elif "左" in bone_name and rot.x() > 0 and fixed_axis.x() < 0: + rot.setX(rot.x() * -1) + rot.setScalar(rot.scalar() * -1) + + rot.normalize() + + # 軸固定の場合、回転を制限する + rot = MQuaternion.fromAxisAndAngle(fixed_axis, rot.toDegree()) + + return rot + + # 指定されたボーンまでの回転量 def calc_direction_qq(model: PmxModel, links: BoneLinks, motion: VmdMotion, fno: int, limit_links=None): - add_qs = calc_relative_rotation(model, links, motion, fno, limit_links) + return c_calc_direction_qq(model, links, motion, fno, limit_links) + +cdef MQuaternion c_calc_direction_qq(PmxModel model, BoneLinks links, VmdMotion motion, int fno, BoneLinks limit_links): + cdef list add_qs = c_calc_relative_rotation(model, links, motion, fno, limit_links) - total_qq = MQuaternion() + cdef MQuaternion total_qq = MQuaternion() for qq in add_qs: total_qq *= qq @@ -430,54 +538,3 @@ def calc_leg_ik_ratio(data_set: MOptionsDataSet): return 1, 1 - -# リンクを元モデルの縮尺に合わせた位置にフィットさせる -def fit_links(org_model: PmxModel, rep_model: PmxModel, rep_links: BoneLinks): - # そのまま弄るとモデルのリンクも変わってしまうので、コピー - fit_rep_links = copy.deepcopy(rep_links) - - # 上半身2の調整 - if fit_rep_links.get("上半身2"): - if org_model.bones["上半身2"] and rep_model.bones["上半身2"] and org_model.bones["上半身"] and rep_model.bones["上半身"] \ - and org_model.bones["左腕"] and rep_model.bones["左腕"]: - - org_upper2_pos = org_model.bones["上半身2"].position - org_upper_pos = org_model.bones["上半身"].position - org_left_arm_pos = org_model.bones["左腕"].position - - rep_upper2_pos = rep_model.bones["上半身2"].position - rep_upper_pos = rep_model.bones["上半身"].position - rep_left_arm_pos = rep_model.bones["左腕"].position - - org_upper_diff = MVector3D(0, org_left_arm_pos.y(), 0) - org_upper_pos - org_upper_diff.setZ(1) - org_upper_diff.abs() - - org_upper2_diff = org_upper2_pos - org_upper_pos - org_upper2_diff.abs() - - rep_upper_diff = MVector3D(0, rep_left_arm_pos.y(), 0) - rep_upper_pos - rep_upper_diff.setZ(1) - rep_upper_diff.abs() - - rep_upper2_diff = rep_upper2_pos - rep_upper_pos - rep_upper2_diff.abs() - - logger.test("org_upper_diff: %s ", org_upper_diff) - logger.test("org_upper2_diff: %s ", org_upper2_diff) - logger.test("rep_upper_diff: %s ", rep_upper_diff) - logger.test("rep_upper2_diff: %s ", rep_upper2_diff) - - upper2_diff_ratio = (org_upper2_diff / org_upper_diff) / (rep_upper2_diff / rep_upper_diff) - logger.test("upper2_diff_ratio: %s ", upper2_diff_ratio) - - # 補正値 - rep_upper2_correction = (rep_upper2_diff * upper2_diff_ratio) - rep_upper2_diff - rep_upper2_correction.effective() - logger.info("rep_upper2_correction: %s ", rep_upper2_correction) - - fit_rep_links.get("上半身2").position += rep_upper2_correction - - return fit_rep_links - - diff --git a/vmdising_np64.spec b/vmdising_np64.spec index 5378dc9..99ec0e6 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,7 +26,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β51_64bit', + name='VmdSizing_5.01_β52_64bit', debug=False, bootloader_ignore_signals=False, strip=False, From 06c372e34f2618fb41d87298231c6820487c2c94 Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Sun, 27 Sep 2020 02:02:57 +0900 Subject: [PATCH 19/37] =?UTF-8?q?5.01=5F=CE=B253?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VmdSizing_5.01_β53 miumiu ・サービスクラス(スタンス追加補正・接触回避・位置合わせ)  ・Cythonに戻した  ・捩り分散も最新の状態に戻った ・ロギング  ・後改行にしてみた --- src/executor.py | 2 +- ...mentService.py => ArmAlignmentService.pyx} | 194 +- ...anceService.py => ArmAvoidanceService.pyx} | 157 +- .../{StanceService.py => StanceService.pyx} | 1817 ++++++++++++----- src/setup_ext.py | 3 +- src/utils/MLogger.py | 2 +- vmdising_np64.spec | 2 +- 7 files changed, 1572 insertions(+), 605 deletions(-) rename src/service/parts/{ArmAlignmentService.py => ArmAlignmentService.pyx} (93%) rename src/service/parts/{ArmAvoidanceService.py => ArmAvoidanceService.pyx} (87%) rename src/service/parts/{StanceService.py => StanceService.pyx} (60%) diff --git a/src/executor.py b/src/executor.py index ac52d38..a3c2e91 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00_β52" +VERSION_NAME = "ver5.00_β53" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/service/parts/ArmAlignmentService.py b/src/service/parts/ArmAlignmentService.pyx similarity index 93% rename from src/service/parts/ArmAlignmentService.py rename to src/service/parts/ArmAlignmentService.pyx index 1a265eb..c0cfa8f 100644 --- a/src/service/parts/ArmAlignmentService.py +++ b/src/service/parts/ArmAlignmentService.pyx @@ -1,27 +1,100 @@ # -*- coding: utf-8 -*- # -import math +import os import numpy as np +import math import itertools +from libc.math cimport sin, cos, acos, atan2, asin, pi, sqrt + +import concurrent.futures +from concurrent.futures import ThreadPoolExecutor + +from module.MParams import BoneLinks # noqa +from module.MParams cimport BoneLinks # noqa + +from module.MMath import MRect, MVector2D, MVector3D, MVector4D, MQuaternion, MMatrix4x4 # noqa +from module.MMath cimport MRect, MVector2D, MVector3D, MVector4D, MQuaternion, MMatrix4x4 # noqa + +from mmd.PmxData import PmxModel, OBB, Bone, Vertex, Material, Morph, DisplaySlot, RigidBody, Joint # noqa +from mmd.PmxData cimport PmxModel, OBB, Bone, RigidBody -from mmd.PmxData import PmxModel, Bone # noqa from mmd.VmdData import VmdMotion, VmdBoneFrame, VmdCameraFrame, VmdInfoIk, VmdLightFrame, VmdMorphFrame, VmdShadowFrame, VmdShowIkFrame # noqa -from module.MMath import MRect, MVector3D, MVector4D, MQuaternion, MMatrix4x4 # noqa +from mmd.VmdData cimport VmdMotion, VmdBoneFrame + from module.MOptions import MOptions, MOptionsDataSet # noqa -from module.MParams import BoneLinks # noqa -from utils import MUtils, MServiceUtils, MBezierUtils # noqa +from module.MOptions cimport MOptions, MOptionsDataSet # noqa + +from utils import MServiceUtils +from utils cimport MServiceUtils + from utils.MLogger import MLogger # noqa from utils.MException import SizingException, MKilledException logger = MLogger(__name__, level=1) # 床処理用INDEX -FLOOR_IDX = -1 -# 近接コピー用ラジアン角度 -LIMIT_COPY_RADIANS = math.cos(math.radians(5)) +cdef int FLOOR_IDX = -1 + + +# 位置合わせ用オプション +cdef class ArmAlignmentOption: + cdef public BoneLinks org_links + cdef public BoneLinks rep_links + cdef public list ik_links_list + cdef public list ik_count_list + cdef public BoneLinks tip_ik_links + cdef public double org_palm_length + cdef public double rep_palm_length + cdef public str start_bone_name + cdef public str effector_bone_name + cdef public str effector_display_bone_name + cdef public double distance + cdef public str tip_bone_name + cdef public int priority + cdef public double ratio + cdef public double multi_ratio + + def __init__(self, org_links: BoneLinks, rep_links: BoneLinks, ik_links_list: list, ik_count_list: list, tip_ik_links: BoneLinks, \ + org_palm_length: float, rep_palm_length: float, org_model: PmxModel, rep_model: PmxModel, start_bone_name: str, \ + effector_bone_name: str, effector_display_bone_name: str, tip_bone_name: str, distance: float, xz_ratio: float, priority: int): + super().__init__() + + self.org_links = org_links + self.rep_links = rep_links + self.ik_links_list = ik_links_list + self.ik_count_list = ik_count_list + self.tip_ik_links = tip_ik_links + self.org_palm_length = org_palm_length + self.rep_palm_length = rep_palm_length + self.start_bone_name = start_bone_name + self.effector_bone_name = effector_bone_name + self.effector_display_bone_name = effector_display_bone_name + self.distance = distance + self.tip_bone_name = tip_bone_name + self.priority = priority + + if "床" in start_bone_name: + # 元と先の比率(床の場合、XZ比率で身体の大きさだけ検討する) + self.ratio = xz_ratio + else: + # エフェクタまでの長さ比率 + org_effector_diff = (org_model.bones[effector_bone_name].position - org_model.bones[start_bone_name].position) + org_effector_diff.one() + rep_effector_diff = (rep_model.bones[effector_bone_name].position - rep_model.bones[start_bone_name].position) + rep_effector_diff.one() + effector_diff_ratio = rep_effector_diff.length() / org_effector_diff.length() + + # 元と先の比率 + self.ratio = effector_diff_ratio + # 元と先の比率(複数モーション時) + self.multi_ratio = xz_ratio -class ArmAlignmentService(): +cdef class ArmAlignmentService: + cdef public object options + cdef public list target_data_set_idxs + cdef public dict target_links + def __init__(self, options: MOptions): self.options = options @@ -71,7 +144,7 @@ def execute(self): # 位置合わせ実行 self.execute_alignment(fnos, all_alignment_group_list, all_messages, bone_names) - + if self.options.now_process_ctrl: self.options.now_process += 1 self.options.now_process_ctrl.write(str(self.options.now_process)) @@ -90,7 +163,24 @@ def execute(self): raise e # 位置合わせ準備 - def prepare_alignment(self, fnos: list): + cdef prepare_alignment(self, list fnos): + cdef int from_data_set_idx, to_data_set_idx, alignment_idx, data_set_idx, fidx, from_alignment_idx + cdef int group_idx, to_alignment_idx, fno, priority, prev_block_fno + cdef double base_distance, distance, distance_ratio, org_palm_mean + cdef dict alignment_options, all_alignment_group, all_distances, all_is_alignment, all_messages, all_org_global_effector_matrixs, all_org_global_effector_vec + cdef dict all_org_global_neck_vec, all_org_global_tip_vec, all_org_global_trunk_matrixs, all_org_global_upper_vec, distances, org_global_3ds, org_global_matrixs + cdef dict all_alignment_idx + cdef list alignment_pairs, all_alignment_group_list, org_effector_pairs, target_pairs + cdef str link_name + cdef bint is_alignment, is_floor, is_sit, prev_from_alignment, prev_to_alignment + cdef VmdBoneFrame bf + cdef MOptionsDataSet data_set, from_data_set, to_data_set + cdef ArmAlignmentOption from_target_link, target_link, to_target_link + cdef BoneLinks ik_links + cdef MMatrix4x4 org_effector_matrix, org_origin_matrix, org_trunk_matrix + cdef MVector3D org_fno_mean_vec, org_from_global_effector_vec, org_global_effector, org_global_tip, org_local_tip, org_mean_vec, org_to_global_effector_vec + cdef MVector3D org_trunk_local_fno_effector, org_trunk_local_fno_origin + all_org_global_effector_vec = {} all_org_global_trunk_matrixs = {} all_org_global_neck_vec = {} @@ -99,7 +189,6 @@ def prepare_alignment(self, fnos: list): all_org_global_effector_matrixs = {} prev_block_fno = 0 - target_pairs = [] for data_set_idx, alignment_options in self.target_links.items(): for alignment_idx, target_link in alignment_options.items(): @@ -124,7 +213,7 @@ def prepare_alignment(self, fnos: list): # 元モデルのそれぞれのグローバル位置 org_global_3ds, org_global_matrixs = \ - MServiceUtils.calc_global_pos(data_set.org_model, target_link.org_links, data_set.org_motion, fno, return_matrix=True, is_local_x=True) + MServiceUtils.c_calc_global_pos(data_set.org_model, target_link.org_links, data_set.org_motion, fno, return_matrix=True, is_local_x=True, limit_links=None) all_org_global_effector_vec[fno][(data_set_idx, alignment_idx)] = org_global_3ds[target_link.effector_bone_name] @@ -490,8 +579,23 @@ def prepare_alignment(self, fnos: list): return all_alignment_group_list, all_messages # 位置合わせ実行 - def execute_alignment(self, fnos: list, all_alignment_group_list: list, all_messages: dict, bone_names: list): - + cdef execute_alignment(self, list fnos, list all_alignment_group_list, dict all_messages, list bone_names): + cdef int data_set_idx, alignment_idx, fidx, fno, ik_cnt, next_success_fno, now_ik_max_count, prev_block_fno, prev_fno, prev_success_fno + cdef str bone_name, link_name + cdef list group_data_set_idxs, next_fnos, next_success_fnos, overwrited, prev_success_fnos, is_success + cdef dict aligned_rep_global_3ds, all_alignment_group + cdef dict dot_far_limit_dict, dot_near_dict, dot_near_limit_dict, dot_start_dict, org_bfs, rep_global_3ds, rep_global_matrixs, results, start_org_bfs + cdef bint is_avoidance_x, is_floor, is_multi + cdef VmdBoneFrame bf, ik_bf, next_bf, prev_bf + cdef MQuaternion correct_qq + cdef MOptionsDataSet data_set + cdef BoneLinks ik_links, now_ik_links + cdef Bone link_bone + cdef MVector3D org_fno_global_effector, org_local_effector, org_local_tip, prev_rep_diff, rep_diff, rep_effector_vec, rep_fno_mean_vec, rep_global_effector, aligned_rep_effector_vec + cdef MVector3D rep_global_tip, rep_local_effector, rep_local_origin, rep_local_tip, rep_target_global_tip, rep_trunk_local_fno_effector, rep_trunk_local_fno_origin + cdef MMatrix4x4 org_origin_matrix, rep_effector_matrix, rep_origin_matrix, rep_trunk_matrix + cdef ArmAlignmentOption target_link + fno = 0 prev_block_fno = 0 for all_alignment_group in all_alignment_group_list: @@ -511,7 +615,7 @@ def execute_alignment(self, fnos: list, all_alignment_group_list: list, all_mess # 先モデルのそれぞれのグローバル位置 rep_global_3ds, rep_global_matrixs = \ - MServiceUtils.calc_global_pos(data_set.rep_model, target_link.rep_links, data_set.motion, fno, return_matrix=True, is_local_x=True) + MServiceUtils.c_calc_global_pos(data_set.rep_model, target_link.rep_links, data_set.motion, fno, return_matrix=True, is_local_x=True, limit_links=None) # キーフレ単位のエフェクタ位置情報 if fno not in all_alignment_group["rep_fno_global_effector"]: @@ -565,8 +669,8 @@ def execute_alignment(self, fnos: list, all_alignment_group_list: list, all_mess # 首根先(体幹の最終的な向き)までの行列 rep_trunk_matrix = all_alignment_group["rep_fno_trunk_matrix"][fno][(data_set_idx, alignment_idx)].copy() - # エフェクタのグローバル位置 - rep_global_effector = all_alignment_group["rep_fno_global_effector"] + # # エフェクタのグローバル位置 + # rep_global_effector = all_alignment_group["rep_fno_global_effector"] # 体幹から見たキーフレ中央値のローカル位置 rep_trunk_local_fno_origin = rep_trunk_matrix.inverted() * rep_fno_mean_vec @@ -686,7 +790,7 @@ def execute_alignment(self, fnos: list, all_alignment_group_list: list, all_mess is_avoidance_x = is_avoidance_x or (bf.avoidance == "x") if is_avoidance_x: - logger.info("-X方向回避済みのため、位置合わせスキップ: f: %s(%s-%s)", fno, (data_set_idx + 1), target_link.effector_display_bone_name) + logger.info("--X方向回避済みのため、位置合わせスキップ: f: %s(%s-%s)", fno, (data_set_idx + 1), target_link.effector_display_bone_name) # IK処理実行 for ik_cnt, (ik_links, ik_max_count) in enumerate(zip(target_link.ik_links_list, target_link.ik_count_list)): @@ -700,10 +804,10 @@ def execute_alignment(self, fnos: list, all_alignment_group_list: list, all_mess list(now_ik_links.all().keys()), rep_effector_vec.to_log(), rep_global_effector.to_log()) # IK計算実行 - MServiceUtils.calc_IK(data_set.rep_model, target_link.rep_links, data_set.motion, fno, rep_global_effector, now_ik_links, max_count=1) + MServiceUtils.c_calc_IK(data_set.rep_model, target_link.rep_links, data_set.motion, fno, rep_global_effector, now_ik_links, max_count=1) # 現在のエフェクタ位置 - aligned_rep_global_3ds = MServiceUtils.calc_global_pos(data_set.rep_model, target_link.rep_links, data_set.motion, fno) + (aligned_rep_global_3ds, _) = MServiceUtils.c_calc_global_pos(data_set.rep_model, target_link.rep_links, data_set.motion, fno, return_matrix=False, is_local_x=False, limit_links=None) aligned_rep_effector_vec = aligned_rep_global_3ds[target_link.effector_bone_name] # 現在のエフェクタ位置との差分(エフェクタ位置が指定されている場合のみ) @@ -997,7 +1101,7 @@ def execute_alignment(self, fnos: list, all_alignment_group_list: list, all_mess # 先モデルのそれぞれのグローバル位置最新データ rep_global_3ds, rep_global_matrixs = \ - MServiceUtils.calc_global_pos(data_set.rep_model, target_link.rep_links, data_set.motion, fno, return_matrix=True, is_local_x=True) + MServiceUtils.c_calc_global_pos(data_set.rep_model, target_link.rep_links, data_set.motion, fno, return_matrix=True, is_local_x=True, limit_links=None) # 指先のローカル位置 org_local_tip = MVector3D(all_alignment_group["org_local_tip"][(fno, data_set_idx, alignment_idx)]) @@ -1036,10 +1140,10 @@ def execute_alignment(self, fnos: list, all_alignment_group_list: list, all_mess list(now_ik_links.all().keys()), rep_global_tip.to_log(), rep_target_global_tip.to_log()) # IK計算実行 - MServiceUtils.calc_IK(data_set.rep_model, target_link.rep_links, data_set.motion, fno, rep_target_global_tip, now_ik_links, max_count=1) + MServiceUtils.c_calc_IK(data_set.rep_model, target_link.rep_links, data_set.motion, fno, rep_target_global_tip, now_ik_links, max_count=1) # 現在のエフェクタ位置 - aligned_rep_global_3ds = MServiceUtils.calc_global_pos(data_set.rep_model, target_link.rep_links, data_set.motion, fno) + aligned_rep_global_3ds, _ = MServiceUtils.c_calc_global_pos(data_set.rep_model, target_link.rep_links, data_set.motion, fno, return_matrix=False, is_local_x=False, limit_links=None) aligned_rep_effector_vec = aligned_rep_global_3ds[target_link.tip_bone_name] # 現在のエフェクタ位置との差分(エフェクタ位置が指定されている場合のみ) @@ -1374,45 +1478,3 @@ def get_target_set_idxs(self): target_data_set_idxs.append(data_set_idx) return target_data_set_idxs - - -# 位置合わせ用オプション -class ArmAlignmentOption(): - - def __init__(self, org_links: BoneLinks, rep_links: BoneLinks, ik_links_list: list, ik_count_list: list, tip_ik_links: BoneLinks, \ - org_palm_length: float, rep_palm_length: float, org_model: PmxModel, rep_model: PmxModel, start_bone_name: str, \ - effector_bone_name: str, effector_display_bone_name: str, tip_bone_name: str, distance: float, xz_ratio: float, priority: int): - super().__init__() - - self.org_links = org_links - self.rep_links = rep_links - self.ik_links_list = ik_links_list - self.ik_count_list = ik_count_list - self.tip_ik_links = tip_ik_links - self.org_palm_length = org_palm_length - self.rep_palm_length = rep_palm_length - self.start_bone_name = start_bone_name - self.effector_bone_name = effector_bone_name - self.effector_display_bone_name = effector_display_bone_name - self.distance = distance - self.tip_bone_name = tip_bone_name - self.priority = priority - - if "床" in start_bone_name: - # 元と先の比率(床の場合、XZ比率で身体の大きさだけ検討する) - self.ratio = xz_ratio - else: - # エフェクタまでの長さ比率 - org_effector_diff = (org_model.bones[effector_bone_name].position - org_model.bones[start_bone_name].position) - org_effector_diff.one() - rep_effector_diff = (rep_model.bones[effector_bone_name].position - rep_model.bones[start_bone_name].position) - rep_effector_diff.one() - effector_diff_ratio = rep_effector_diff.length() / org_effector_diff.length() - - # 元と先の比率 - self.ratio = effector_diff_ratio - # 元と先の比率(複数モーション時) - self.multi_ratio = xz_ratio - - - diff --git a/src/service/parts/ArmAvoidanceService.py b/src/service/parts/ArmAvoidanceService.pyx similarity index 87% rename from src/service/parts/ArmAvoidanceService.py rename to src/service/parts/ArmAvoidanceService.pyx index a3f5ec1..9dde672 100644 --- a/src/service/parts/ArmAvoidanceService.py +++ b/src/service/parts/ArmAvoidanceService.pyx @@ -2,25 +2,45 @@ # import os import numpy as np +import math +from libc.math cimport sin, cos, acos, atan2, asin, pi, sqrt + import concurrent.futures from concurrent.futures import ThreadPoolExecutor -from mmd.PmxData import PmxModel, Bone # noqa +from module.MParams import BoneLinks # noqa +from module.MParams cimport BoneLinks # noqa + +from module.MMath import MRect, MVector2D, MVector3D, MVector4D, MQuaternion, MMatrix4x4 # noqa +from module.MMath cimport MRect, MVector2D, MVector3D, MVector4D, MQuaternion, MMatrix4x4 # noqa + +from mmd.PmxData import PmxModel, OBB, Bone, Vertex, Material, Morph, DisplaySlot, RigidBody, Joint # noqa +from mmd.PmxData cimport PmxModel, OBB, Bone, RigidBody + from mmd.VmdData import VmdMotion, VmdBoneFrame, VmdCameraFrame, VmdInfoIk, VmdLightFrame, VmdMorphFrame, VmdShadowFrame, VmdShowIkFrame # noqa -from module.MMath import MRect, MVector3D, MVector4D, MQuaternion, MMatrix4x4 # noqa +from mmd.VmdData cimport VmdMotion, VmdBoneFrame + from module.MOptions import MOptions, MOptionsDataSet # noqa -from module.MParams import BoneLinks # noqa -from utils import MUtils, MServiceUtils, MBezierUtils # noqa +from module.MOptions cimport MOptions, MOptionsDataSet # noqa + +from utils import MServiceUtils +from utils cimport MServiceUtils + from utils.MLogger import MLogger # noqa from utils.MException import SizingException, MKilledException logger = MLogger(__name__, level=1) - # 接触回避用オプション -class ArmAvoidanceOption(): - - def __init__(self, arm_links: BoneLinks, ik_links_list: list, ik_count_list: list, avoidance_links: dict, avoidances: dict, face_length: float): +cdef class ArmAvoidanceOption: + cdef public list arm_links + cdef public dict ik_links_list + cdef public dict ik_count_list + cdef public dict avoidance_links + cdef public dict avoidances + cdef public double face_length + + def __init__(self, arm_links: list, ik_links_list: dict, ik_count_list: dict, avoidance_links: dict, avoidances: dict, face_length: float): super().__init__() self.arm_links = arm_links @@ -31,7 +51,11 @@ def __init__(self, arm_links: BoneLinks, ik_links_list: list, ik_count_list: lis self.face_length = face_length -class ArmAvoidanceService(): +cdef class ArmAvoidanceService: + cdef public object options + cdef public list target_data_set_idxs + cdef public dict avoidance_options + def __init__(self, options: MOptions): self.options = options @@ -54,22 +78,33 @@ def execute(self): self.avoidance_options[(data_set_idx, "左")] = self.prepare_avoidance(data_set_idx, "左") self.avoidance_options[(data_set_idx, "右")] = self.prepare_avoidance(data_set_idx, "右") - futures = [] - with ThreadPoolExecutor(thread_name_prefix="avoidance", max_workers=self.options.max_workers) as executor: - for data_set_idx, data_set in enumerate(self.options.data_set_list): - futures.append(executor.submit(self.execute_avoidance_pool, data_set_idx, "右")) - futures.append(executor.submit(self.execute_avoidance_pool, data_set_idx, "左")) + for data_set_idx, data_set in enumerate(self.options.data_set_list): + self.execute_avoidance_pool(data_set_idx, "右") + self.execute_avoidance_pool(data_set_idx, "左") - concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) + if self.options.now_process_ctrl: + self.options.now_process += 1 + self.options.now_process_ctrl.write(str(self.options.now_process)) + + proccess_key = "【No.{0}】{1}({2})".format(data_set_idx + 1, os.path.basename(data_set.motion.path), data_set.rep_model.name) + self.options.tree_process_dict[proccess_key]["接触回避"] = True - for f in futures: - if not f.result(): - return False + # futures = [] + # with ThreadPoolExecutor(thread_name_prefix="avoidance", max_workers=self.options.max_workers) as executor: + # for data_set_idx, data_set in enumerate(self.options.data_set_list): + # futures.append(executor.submit(self.execute_avoidance_pool, data_set_idx, "右")) + # futures.append(executor.submit(self.execute_avoidance_pool, data_set_idx, "左")) + + # concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) + + # for f in futures: + # if not f.result(): + # return False return True # 接触回避 - def execute_avoidance_pool(self, data_set_idx: int, direction: str): + cpdef bint execute_avoidance_pool(self, int data_set_idx, str direction): try: # 接触回避準備 all_avoidance_axis = self.prepare_avoidance_dataset(data_set_idx, direction) @@ -77,6 +112,14 @@ def execute_avoidance_pool(self, data_set_idx: int, direction: str): # 接触回避処理 self.execute_avoidance(data_set_idx, direction, all_avoidance_axis) + # # 接触回避準備 + # pfun = profile(self.prepare_avoidance_dataset) + # all_avoidance_axis = pfun(data_set_idx, direction) + + # # 接触回避処理 + # pfun = profile(self.execute_avoidance) + # pfun(data_set_idx, direction, all_avoidance_axis) + # # 各ボーンのbfを円滑化 # futures = [] # with ThreadPoolExecutor(thread_name_prefix="avoidance_after{0}".format(data_set_idx)) as executor: @@ -88,15 +131,6 @@ def execute_avoidance_pool(self, data_set_idx: int, direction: str): # if not f.result(): # return False - data_set = self.options.data_set_list[data_set_idx] - - if self.options.now_process_ctrl and direction == "右": - self.options.now_process += 1 - self.options.now_process_ctrl.write(str(self.options.now_process)) - - proccess_key = "【No.{0}】{1}({2})".format(data_set_idx + 1, os.path.basename(data_set.motion.path), data_set.rep_model.name) - self.options.tree_process_dict[proccess_key]["接触回避"] = True - return True except MKilledException as ke: raise ke @@ -140,7 +174,22 @@ def execute_avoidance_after(self, data_set_idx: int, bone_name: str): raise e # 接触回避処理 - def execute_avoidance(self, data_set_idx: int, direction: str, all_avoidance_axis: dict): + cpdef bint execute_avoidance(self, int data_set_idx, str direction, dict all_avoidance_axis): + cdef int fno, ik_cnt, ik_max_count, now_ik_max_count, prev_block_fno + cdef str arm_bone_name, avoidance_name, bone_name, elbow_bone_name, link_name, wrist_bone_name, axis + cdef list fnos, ik_links_list, target_bone_names, is_success, failured_last_names + cdef dict dot_dict, dot_limit_dict, now_rep_global_3ds, org_bfs, rep_avbone_global_3ds, rep_avbone_global_mats, rep_global_3ds, avoidance_axis + cdef bint is_in_elbow + cdef MVector3D now_rep_effector_pos, rep_collision_vec, rep_diff, prev_rep_diff + cdef MOptionsDataSet data_set + cdef BoneLinks arm_link, avodance_link, ik_links + cdef VmdBoneFrame arm_bf, bf, elbow_bf, now_bf + cdef RigidBody avoidance + cdef ArmAvoidanceOption avoidance_options + cdef MQuaternion elbow_adjust_qq + cdef Bone link_bone + cdef OBB obb + logger.info("接触回避処理【No.%s - %s】", (data_set_idx + 1), direction) logger.copy(self.options) @@ -161,7 +210,7 @@ def execute_avoidance(self, data_set_idx: int, direction: str, all_avoidance_axi # 一度全部キーを追加する(キー自体は無効化のまま) for fno in fnos: - for bone_name in target_bone_names: + for bone_name in [arm_bone_name, elbow_bone_name]: if bone_name not in data_set.motion.bones: data_set.motion.bones[bone_name] = {} data_set.motion.bones[bone_name][fno] = data_set.motion.calc_bf(bone_name, fno) @@ -175,8 +224,8 @@ def execute_avoidance(self, data_set_idx: int, direction: str, all_avoidance_axi for ((avoidance_name, avodance_link), avoidance) in zip(avoidance_options.avoidance_links.items(), avoidance_options.avoidances.values()): # 剛体の現在位置をチェック - rep_avbone_global_3ds, rep_avbone_global_mats = \ - MServiceUtils.calc_global_pos(data_set.rep_model, avodance_link, data_set.motion, fno, return_matrix=True) + (rep_avbone_global_3ds, rep_avbone_global_mats) = \ + MServiceUtils.c_calc_global_pos(data_set.rep_model, avodance_link, data_set.motion, fno, return_matrix=True, is_local_x=False, limit_links=None) obb = avoidance.get_obb(fno, avodance_link.get(avodance_link.last_name()).position, rep_avbone_global_mats, self.options.arm_options.alignment, direction == "左") @@ -196,11 +245,12 @@ def execute_avoidance(self, data_set_idx: int, direction: str, all_avoidance_axi for arm_link in avoidance_options.arm_links: # 先モデルのそれぞれのグローバル位置 - rep_global_3ds = MServiceUtils.calc_global_pos(data_set.rep_model, arm_link, data_set.motion, fno) + (rep_global_3ds, _) = \ + MServiceUtils.c_calc_global_pos(data_set.rep_model, arm_link, data_set.motion, fno, return_matrix=False, is_local_x=False, limit_links=None) # [logger.test("f: %s, k: %s, v: %s", fno, k, v) for k, v in rep_global_3ds.items()] # 衝突情報を取る - collision, near_collision, x_distance, z_distance, rep_x_collision_vec, rep_z_collision_vec \ + (collision, near_collision, x_distance, z_distance, rep_x_collision_vec, rep_z_collision_vec) \ = obb.get_collistion(rep_global_3ds[arm_link.last_name()], rep_global_3ds[arm_bone_name], \ data_set.rep_model.bones[arm_bone_name].position.distanceToPoint(data_set.rep_model.bones[arm_link.last_name()].position)) @@ -261,10 +311,11 @@ def execute_avoidance(self, data_set_idx: int, direction: str, all_avoidance_axi list(ik_links.all().keys()), avoidance_name, axis, rep_global_3ds[arm_link.last_name()].to_log(), rep_collision_vec.to_log()) # 修正角度がない場合、IK計算実行 - MServiceUtils.calc_IK(data_set.rep_model, arm_link, data_set.motion, fno, rep_collision_vec, ik_links, max_count=1) + MServiceUtils.c_calc_IK(data_set.rep_model, arm_link, data_set.motion, fno, rep_collision_vec, ik_links, max_count=1) # 現在のエフェクタ位置 - now_rep_global_3ds = MServiceUtils.calc_global_pos(data_set.rep_model, arm_link, data_set.motion, fno) + (now_rep_global_3ds, _) = \ + MServiceUtils.c_calc_global_pos(data_set.rep_model, arm_link, data_set.motion, fno, return_matrix=False, is_local_x=False, limit_links=None) now_rep_effector_pos = now_rep_global_3ds[arm_link.last_name()] # 現在のエフェクタ位置との差分(エフェクタ位置が指定されている場合のみ) @@ -315,7 +366,7 @@ def execute_avoidance(self, data_set_idx: int, direction: str, all_avoidance_axi is_success = [True] # 衝突を計り直す - collision, near_collision, x_distance, z_distance, rep_x_collision_vec, rep_z_collision_vec \ + (collision, near_collision, x_distance, z_distance, rep_x_collision_vec, rep_z_collision_vec) \ = obb.get_collistion(now_rep_global_3ds[arm_link.last_name()], now_rep_global_3ds[arm_link.first_name()], \ data_set.rep_model.bones[arm_bone_name].position.distanceToPoint(data_set.rep_model.bones[arm_link.last_name()].position)) @@ -357,7 +408,7 @@ def execute_avoidance(self, data_set_idx: int, direction: str, all_avoidance_axi # 採用されたらOK is_success.append(True) # 衝突を計り直す - collision, near_collision, x_distance, z_distance, rep_x_collision_vec, rep_z_collision_vec \ + (collision, near_collision, x_distance, z_distance, rep_x_collision_vec, rep_z_collision_vec) \ = obb.get_collistion(now_rep_global_3ds[arm_link.last_name()], now_rep_global_3ds[arm_link.first_name()], \ data_set.rep_model.bones[arm_bone_name].position.distanceToPoint(data_set.rep_model.bones[arm_link.last_name()].position)) @@ -465,9 +516,22 @@ def execute_avoidance(self, data_set_idx: int, direction: str, all_avoidance_axi return True # 接触回避準備 - def prepare_avoidance_dataset(self, data_set_idx: int, direction: str): + cpdef dict prepare_avoidance_dataset(self, int data_set_idx, str direction): logger.info("接触回避準備【No.%s - %s】", (data_set_idx + 1), direction) + cdef int aidx, fno, from_fno, prev_block_fno, to_fno + cdef double block_x_distance, block_z_distance, x_distance, z_distance + cdef list all_avoidance_list, fnos, prev_collisions + cdef dict all_avoidance_axis, rep_avbone_global_3ds, rep_avbone_global_mats, rep_global_3ds, rep_matrixs, avoidance_list + cdef str avoidance_name, bone_name + cdef bint collision, near_collision + cdef BoneLinks arm_link, avodance_link + cdef ArmAvoidanceOption avoidance_options + cdef MOptionsDataSet data_set + cdef OBB obb + cdef RigidBody avoidance + cdef MVector3D rep_x_collision_vec, rep_z_collision_vec + logger.copy(self.options) # 処理対象データセット data_set = self.options.data_set_list[data_set_idx] @@ -491,18 +555,19 @@ def prepare_avoidance_dataset(self, data_set_idx: int, direction: str): for ((avoidance_name, avodance_link), avoidance) in zip(avoidance_options.avoidance_links.items(), avoidance_options.avoidances.values()): # 剛体の現在位置をチェック - rep_avbone_global_3ds, rep_avbone_global_mats = \ - MServiceUtils.calc_global_pos(data_set.rep_model, avodance_link, data_set.motion, fno, return_matrix=True) + (rep_avbone_global_3ds, rep_avbone_global_mats) = \ + MServiceUtils.c_calc_global_pos(data_set.rep_model, avodance_link, data_set.motion, fno, return_matrix=True, is_local_x=False, limit_links=None) obb = avoidance.get_obb(fno, avodance_link.get(avodance_link.last_name()).position, rep_avbone_global_mats, self.options.arm_options.alignment, direction == "左") for arm_link in avoidance_options.arm_links: # 先モデルのそれぞれのグローバル位置 - rep_global_3ds, rep_matrixs = MServiceUtils.calc_global_pos(data_set.rep_model, arm_link, data_set.motion, fno, return_matrix=True) - # [logger.test("f: %s, k: %s, v: %s", fno, k, v) for k, v in rep_global_3ds.items()] + (rep_global_3ds, rep_matrixs) = \ + MServiceUtils.c_calc_global_pos(data_set.rep_model, arm_link, data_set.motion, fno, return_matrix=True, is_local_x=False, limit_links=None) + # [logger.debug("f: %s, k: %s, v: %s", fno, k, v) for k, v in rep_global_3ds.items()] # 衝突情報を取る - collision, near_collision, x_distance, z_distance, rep_x_collision_vec, rep_z_collision_vec \ + (collision, near_collision, x_distance, z_distance, rep_x_collision_vec, rep_z_collision_vec) \ = obb.get_collistion(rep_global_3ds[arm_link.last_name()], rep_global_3ds["{0}腕".format(direction)], \ data_set.rep_model.bones["{0}腕".format(direction)].position.distanceToPoint(data_set.rep_model.bones[arm_link.last_name()].position)) @@ -549,7 +614,7 @@ def prepare_avoidance_dataset(self, data_set_idx: int, direction: str): # トータルで近い方の軸に移動させる all_avoidance_axis[from_fno] = {"from_fno": from_fno, "to_fno": to_fno, "axis": ("x" if block_x_distance * 1.5 < block_z_distance else "z")} logger.debug("aidx: %s, d: %s, from: %s, to: %s, axis: %s, xd: %s, zd: %s", aidx, direction, from_fno, to_fno, all_avoidance_axis[from_fno], block_x_distance, block_z_distance) - + if fno // 1000 > prev_block_fno and fnos[-1] > 0: logger.info("-- %sフレーム目:終了(%s%)【No.%s - 接触回避準備② - %s】", fno, round((fno / fnos[-1]) * 100, 3), data_set_idx + 1, direction) prev_block_fno = fno // 1000 @@ -587,7 +652,7 @@ def prepare_avoidance(self, data_set_idx: int, direction: str): avoidance_links = {} avoidances = {} - if "頭接触回避" in self.options.arm_options.avoidance_target_list[data_set_idx]: + if data_set_idx in self.options.arm_options.avoidance_target_list and "頭接触回避" in self.options.arm_options.avoidance_target_list[data_set_idx]: # 頭接触回避用剛体取得 head_rigidbody = data_set.rep_model.get_head_rigidbody() head_rigidbody.is_small = (face_length <= 3) diff --git a/src/service/parts/StanceService.py b/src/service/parts/StanceService.pyx similarity index 60% rename from src/service/parts/StanceService.py rename to src/service/parts/StanceService.pyx index 5a045ba..9ef1048 100644 --- a/src/service/parts/StanceService.py +++ b/src/service/parts/StanceService.pyx @@ -3,31 +3,54 @@ import os import numpy as np import math +from libc.math cimport sin, cos, acos, atan2, asin, pi, sqrt + import concurrent.futures from concurrent.futures import ThreadPoolExecutor -from mmd.PmxData import PmxModel # noqa +from module.MParams import BoneLinks # noqa +from module.MParams cimport BoneLinks # noqa + +from module.MMath import MRect, MVector2D, MVector3D, MVector4D, MQuaternion, MMatrix4x4 # noqa +from module.MMath cimport MRect, MVector2D, MVector3D, MVector4D, MQuaternion, MMatrix4x4 # noqa + +from mmd.PmxData import PmxModel, OBB, Bone, Vertex, Material, Morph, DisplaySlot, RigidBody, Joint # noqa +from mmd.PmxData cimport PmxModel, OBB, Bone, RigidBody + from mmd.VmdData import VmdMotion, VmdBoneFrame, VmdCameraFrame, VmdInfoIk, VmdLightFrame, VmdMorphFrame, VmdShadowFrame, VmdShowIkFrame # noqa -from module.MMath import MRect, MVector3D, MVector4D, MQuaternion, MMatrix4x4 # noqa -from module.MOptions import MOptions, MOptionsDataSet -from module.MParams import BoneLinks -from utils import MUtils, MServiceUtils, MBezierUtils # noqa +from mmd.VmdData cimport VmdMotion, VmdBoneFrame + +from module.MOptions import MOptions, MOptionsDataSet # noqa +from module.MOptions cimport MOptions, MOptionsDataSet # noqa + +from utils import MServiceUtils, MBezierUtils +from utils cimport MServiceUtils + from utils.MLogger import MLogger # noqa from utils.MException import SizingException, MKilledException logger = MLogger(__name__, level=MLogger.DEBUG) -RADIANS_1 = math.cos(math.radians(1)) -RADIANS_2 = math.cos(math.radians(2)) -RADIANS_5 = math.cos(math.radians(5)) -RADIANS_8 = math.cos(math.radians(8)) +cdef double RADIANS_01 = cos(math.radians(0.1)) +cdef double RADIANS_05 = cos(math.radians(0.5)) +cdef double RADIANS_1 = cos(math.radians(1)) +cdef double RADIANS_2 = cos(math.radians(2)) +cdef double RADIANS_5 = cos(math.radians(5)) +cdef double RADIANS_8 = cos(math.radians(8)) +cdef double RADIANS_12 = cos(math.radians(12)) +cdef double RADIANS_15 = cos(math.radians(15)) +cdef class StanceService(): + + cdef public object options -class StanceService(): def __init__(self, options: MOptions): self.options = options def execute(self): + # for data_set_idx, data_set in enumerate(self.options.data_set_list): + # self.execute_pool(data_set_idx) + futures = [] with ThreadPoolExecutor(thread_name_prefix="stance", max_workers=min(5, self.options.max_workers)) as executor: for data_set_idx, data_set in enumerate(self.options.data_set_list): @@ -35,7 +58,7 @@ def execute(self): # モーションデータが無い場合、処理スキップ continue - futures.append(executor.submit(self.execute_pool, data_set_idx)) + futures.append(executor.submit(self.execute_pool, self, data_set_idx)) concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) @@ -45,7 +68,10 @@ def execute(self): return True - def execute_pool(self, data_set_idx: int): + cdef bint execute_pool(self, int data_set_idx): + cdef MOptionsDataSet data_set + cdef bint result = True + try: logger.copy(self.options) data_set = self.options.data_set_list[data_set_idx] @@ -54,47 +80,47 @@ def execute_pool(self, data_set_idx: int): if data_set.detail_stance_flg: if "センターXZ補正" in data_set.selected_stance_details: # センターXZ補正 - self.adjust_center_stance(data_set_idx, data_set) + result = result and self.adjust_center_stance(data_set_idx, data_set) - if "上半身補正" in data_set.selected_stance_details: + if result and "上半身補正" in data_set.selected_stance_details: # 上半身補正 - self.adjust_upper_stance(data_set_idx, data_set) + result = result and self.adjust_upper_stance(data_set_idx, data_set) - if "下半身補正" in data_set.selected_stance_details: + if result and "下半身補正" in data_set.selected_stance_details: # 下半身補正 - self.adjust_lower_stance(data_set_idx, data_set) + result = result and self.adjust_lower_stance(data_set_idx, data_set) - if "足IK補正" in data_set.selected_stance_details: + if result and "足IK補正" in data_set.selected_stance_details: # 足IK補正 - self.adjust_leg_ik_stance(data_set_idx, data_set) + result = result and self.adjust_leg_ik_stance(data_set_idx, data_set) - if "つま先補正" in data_set.selected_stance_details: + if result and "つま先補正" in data_set.selected_stance_details: # つま先補正 - self.adjust_toe_stance(data_set_idx, data_set) + result = result and self.adjust_toe_stance(data_set_idx, data_set) - if "つま先IK補正" in data_set.selected_stance_details: + if result and "つま先IK補正" in data_set.selected_stance_details: # つま先IK補正 - self.adjust_toe_ik_stance(data_set_idx, data_set) + result = result and self.adjust_toe_ik_stance(data_set_idx, data_set) # 腕系サイジング可能(もしくはチェックスキップ)であれば、腕スタンス補正 if (data_set.org_model.can_arm_sizing and data_set.rep_model.can_arm_sizing) or self.options.arm_options.arm_check_skip_flg: if data_set.detail_stance_flg: - if "肩補正" in data_set.selected_stance_details: + if result and "肩補正" in data_set.selected_stance_details: # 肩補正 - self.adjust_shoulder_stance(data_set_idx, data_set) - + result = result and self.adjust_shoulder_stance(data_set_idx, data_set) + # 腕スタンス補正 - self.adjust_arm_stance(data_set_idx, data_set) + result = result and self.adjust_arm_stance(data_set_idx, data_set) - if data_set.twist_flg: + if result and data_set.twist_flg: # 捩り分散あり - self.spread_twist(data_set_idx, data_set) - + result = result and self.spread_twist(data_set_idx, data_set) + if data_set.detail_stance_flg: # センターY補正 - if "センターY補正" in data_set.selected_stance_details: + if result and "センターY補正" in data_set.selected_stance_details: # センターY補正 - self.adjust_center_arm_stance(data_set_idx, data_set) + result = result and self.adjust_center_arm_stance(data_set_idx, data_set) else: target_model_type = "" @@ -121,19 +147,22 @@ def execute_pool(self, data_set_idx: int): raise e # 捩り分散 - def spread_twist(self, data_set_idx: int, data_set: MOptionsDataSet): + cdef bint spread_twist(self, int data_set_idx, data_set): logger.info("捩り分散 【No.%s】", (data_set_idx + 1), decoration=MLogger.DECORATION_LINE) + + # for direction in ["左", "右"]: + # self.spread_twist_lr(data_set_idx, direction) futures = [] with ThreadPoolExecutor(thread_name_prefix="twist{0}".format(data_set_idx), max_workers=min(5, self.options.max_workers)) as executor: for direction in ["左", "右"]: - futures.append(executor.submit(self.spread_twist_lr, data_set_idx, direction)) + futures.append(executor.submit(self.spread_twist_lr, self, data_set_idx, direction, True)) concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) for f in futures: if not f.result(): return False - + if self.options.now_process_ctrl: self.options.now_process += 1 self.options.now_process_ctrl.write(str(self.options.now_process)) @@ -144,7 +173,17 @@ def spread_twist(self, data_set_idx: int, data_set: MOptionsDataSet): return True # 捩り分散左右 - def spread_twist_lr(self, data_set_idx: int, direction: str): + cdef bint spread_twist_lr(self, int data_set_idx, str direction, bint dummy): + cdef str arm_bone_name, arm_twist_bone_name, elbow_bone_name, wrist_twist_bone_name, wrist_bone_name, bone_name + cdef MVector3D local_z_axis, arm_local_x_axis, arm_twist_local_x_axis, elbow_local_x_axis, elbow_local_y_axis, arm_local_z2y_axis, arm_local_y_axis + cdef MVector3D wrist_twist_local_x_axis, wrist_local_x_axis, wrist_local_y_axis + cdef list twist_target_bones, fnos, log_target_idxs, new_fnos, check_fnos + cdef set append_fnos + cdef int prev_sep_fno, fno_idx, fno, prev_fno, next_fno, d + cdef MOptionsDataSet data_set + cdef MQuaternion arm_z2y_qq + cdef VmdMotion prev_twist_motion + try: logger.copy(self.options) data_set = self.options.data_set_list[data_set_idx] @@ -167,12 +206,23 @@ def spread_twist_lr(self, data_set_idx: int, direction: str): # 各ボーンのローカル軸 local_z_axis = MVector3D(0, 0, -1) arm_local_x_axis = data_set.rep_model.get_local_x_axis(arm_bone_name) + arm_local_y_axis = MVector3D.crossProduct(arm_local_x_axis, local_z_axis).normalized() arm_twist_local_x_axis = data_set.rep_model.get_local_x_axis(arm_twist_bone_name) + arm_twist_local_y_axis = MVector3D.crossProduct(arm_twist_local_x_axis, local_z_axis).normalized() elbow_local_x_axis = data_set.rep_model.get_local_x_axis(elbow_bone_name) elbow_local_y_axis = MVector3D.crossProduct(elbow_local_x_axis, local_z_axis).normalized() wrist_twist_local_x_axis = data_set.rep_model.get_local_x_axis(wrist_twist_bone_name) + wrist_twist_local_y_axis = MVector3D.crossProduct(wrist_twist_local_x_axis, local_z_axis).normalized() wrist_local_x_axis = data_set.rep_model.get_local_x_axis(wrist_bone_name) wrist_local_y_axis = MVector3D.crossProduct(wrist_local_x_axis, local_z_axis).normalized() + # elbow_y2z_qq = MQuaternion.rotationTo(MVector3D(0, 0, -1 * np.sign(elbow_local_x_axis.x())), elbow_local_y_axis) + elbow_y2z_qq = MQuaternion.rotationTo(elbow_local_y_axis, local_z_axis) + elbow_local_z2y_axis = elbow_y2z_qq * elbow_local_y_axis + + # 腕のスタンス差 + arm_diff_qq_dic = self.calc_arm_stance(data_set) + elbow_stance_degree = (arm_diff_qq_dic[elbow_bone_name]["from"].inverted() * arm_diff_qq_dic[elbow_bone_name]["to"]).toDegree() + logger.debug("%s: elbow_stance_degree: %s, from: %s, to: %s", elbow_bone_name, elbow_stance_degree, arm_diff_qq_dic[elbow_bone_name]["from"].toDegree(), arm_diff_qq_dic[elbow_bone_name]["to"].toDegree()) logger.test("%s: axis: %s", arm_bone_name, arm_local_x_axis) logger.test("%s: axis: %s", arm_twist_bone_name, arm_twist_local_x_axis) @@ -184,12 +234,25 @@ def spread_twist_lr(self, data_set_idx: int, direction: str): # 内積差分に基づきキー追加 logger.info("%s捩り分散準備開始【No.%s】", direction, (data_set_idx + 1)) fnos = data_set.motion.get_differ_fnos((data_set_idx + 1), [arm_bone_name, arm_twist_bone_name, elbow_bone_name, wrist_twist_bone_name, wrist_bone_name], \ - limit_degrees=20, limit_length=0) + limit_degrees=70, limit_length=0) + + futures = [] + with ThreadPoolExecutor(thread_name_prefix="twist_regist{0}".format(data_set_idx), max_workers=self.options.max_workers) as executor: + for bone_name in [arm_bone_name, elbow_bone_name, wrist_bone_name]: + futures.append(executor.submit(self.regist_twist_bf, self, data_set_idx, bone_name, fnos, None)) + + concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) + for f in futures: + if not f.result(): + return False + + # 捩り前のを保持 + prev_twist_motion = data_set.motion.copy() futures = [] - with ThreadPoolExecutor(thread_name_prefix="twist_smooth{0}".format(data_set_idx), max_workers=self.options.max_workers) as executor: - for bone_name in [arm_bone_name, arm_twist_bone_name, elbow_bone_name, wrist_twist_bone_name, wrist_bone_name]: - futures.append(executor.submit(self.regist_twist_bf, data_set_idx, bone_name, fnos)) + with ThreadPoolExecutor(thread_name_prefix="twist_regist{0}".format(data_set_idx), max_workers=self.options.max_workers) as executor: + futures.append(executor.submit(self.regist_twist_bf, self, data_set_idx, arm_twist_bone_name, fnos, arm_bone_name)) + futures.append(executor.submit(self.regist_twist_bf, self, data_set_idx, wrist_twist_bone_name, fnos, wrist_bone_name)) concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) for f in futures: @@ -215,54 +278,144 @@ def spread_twist_lr(self, data_set_idx: int, direction: str): futures = [] with ThreadPoolExecutor(thread_name_prefix="twist_exec{0}".format(data_set_idx), max_workers=min(5, self.options.max_workers)) as executor: for fno_idx, fno in enumerate(fnos): - futures.append(executor.submit(self.spread_twist_pool, data_set_idx, fno_idx, fno, fnos[-1], \ + futures.append(executor.submit(self.spread_twist_pool, self, data_set_idx, fno_idx, fno, fnos[-1], \ arm_bone_name, arm_twist_bone_name, elbow_bone_name, wrist_twist_bone_name, wrist_bone_name, \ - arm_local_x_axis, arm_twist_local_x_axis, elbow_local_x_axis, elbow_local_y_axis, \ - wrist_twist_local_x_axis, wrist_local_x_axis, wrist_local_y_axis, log_target_idxs)) + arm_local_x_axis, arm_local_y_axis, arm_twist_local_x_axis, arm_twist_local_y_axis, elbow_local_x_axis, elbow_local_y_axis, \ + wrist_twist_local_x_axis, wrist_twist_local_y_axis, wrist_local_x_axis, wrist_local_y_axis, \ + elbow_y2z_qq, elbow_local_z2y_axis, elbow_stance_degree, log_target_idxs)) concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) for f in futures: if not f.result(): return False + + logger.info("%s捩り分散後処理 - 分散中間チェック①【No.%s】", arm_bone_name, (data_set_idx + 1)) + + check_fnos = [] + futures = [] + with ThreadPoolExecutor(thread_name_prefix="twist_exec{0}".format(data_set_idx), max_workers=min(5, self.options.max_workers)) as executor: + # 分散後にフリップ起こしてないかチェック + for fno_idx, (prev_fno, next_fno) in enumerate(zip(fnos[:-1], fnos[1:])): + fno = int(prev_fno + ((next_fno - prev_fno) / 2)) + if fno not in fnos: + check_fnos.append(fno) + + check_fnos = list(sorted(list(set(check_fnos)))) + + if len(check_fnos) > 0: + prev_sep_fno = 0 + log_target_idxs = [] + for fno_idx, fno in enumerate(check_fnos): + if fno // 1000 > prev_sep_fno: + log_target_idxs.append(fno) + prev_sep_fno = fno // 1000 + log_target_idxs.append(check_fnos[-1]) + + for fno in check_fnos: + futures.append(executor.submit(self.check_twist_pool, self, data_set_idx, prev_twist_motion, fno_idx, fno, check_fnos[-1], \ + arm_bone_name, arm_twist_bone_name, elbow_bone_name, wrist_twist_bone_name, wrist_bone_name, \ + arm_local_x_axis, arm_local_y_axis, arm_twist_local_x_axis, arm_twist_local_y_axis, elbow_local_x_axis, elbow_local_y_axis, \ + wrist_twist_local_x_axis, wrist_twist_local_y_axis, wrist_local_x_axis, wrist_local_y_axis, \ + elbow_y2z_qq, elbow_local_z2y_axis, elbow_stance_degree, log_target_idxs, "①")) + concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) + + for f in futures: + if not f.result(): + return False + + # # 腕系ボーンのfnos再取得 + # new_fnos = data_set.motion.get_bone_fnos(arm_bone_name, arm_twist_bone_name, elbow_bone_name, wrist_twist_bone_name, wrist_bone_name) + + # logger.info("%s捩り分散後処理 - 分散中間チェック②【No.%s】", arm_bone_name, (data_set_idx + 1)) + + # append_fnos = set(fnos) ^ set(new_fnos) + + # if len(append_fnos) > 0: + # check_fnos = [] + # futures = [] + # with ThreadPoolExecutor(thread_name_prefix="twist_exec{0}".format(data_set_idx), max_workers=min(5, self.options.max_workers)) as executor: + # # 最初のfnoリストに含まれず、チェック後のfnoリストに含まれており、かつ新fnoがまだ追加されていない場合のみチェック + # # よっぽどじゃないと引っかからないけど、ここまでひっかっかってたら全打ちに近い形にしないと無理 + # for fno_idx, (prev_fno, next_fno) in enumerate(zip(new_fnos[:-1], new_fnos[1:])): + # if (prev_fno in append_fnos or next_fno in append_fnos): + # for d in range(0, int((next_fno - prev_fno) / 2) + 1): + # for fno in [int(prev_fno + ((next_fno - prev_fno) / 2) + d), int(prev_fno + ((next_fno - prev_fno) / 2) - d)]: + # if fno not in new_fnos: + # check_fnos.append(fno) + + # check_fnos = list(sorted(list(set(check_fnos)))) + + # if len(check_fnos) > 0: + # prev_sep_fno = 0 + # log_target_idxs = [] + # for fno_idx, fno in enumerate(check_fnos): + # if fno // 1000 > prev_sep_fno: + # log_target_idxs.append(fno) + # prev_sep_fno = fno // 1000 + # log_target_idxs.append(check_fnos[-1]) + + # for fno in check_fnos: + # futures.append(executor.submit(self.check_twist_pool, self, data_set_idx, prev_twist_motion, fno_idx, fno, check_fnos[-1], \ + # arm_bone_name, arm_twist_bone_name, elbow_bone_name, wrist_twist_bone_name, wrist_bone_name, \ + # arm_local_x_axis, arm_local_y_axis, arm_twist_local_x_axis, arm_twist_local_y_axis, elbow_local_x_axis, elbow_local_y_axis, \ + # wrist_twist_local_x_axis, wrist_twist_local_y_axis, wrist_local_x_axis, wrist_local_y_axis, \ + # elbow_y2z_qq, elbow_local_z2y_axis, elbow_stance_degree, log_target_idxs, "②")) + # concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) + + # for f in futures: + # if not f.result(): + # return False # logger.info("%s捩り分散後処理 - 円滑化【No.%s】", arm_bone_name, (data_set_idx + 1)) - # # 各ボーンのbfを円滑化 + # # 捩りボーンのbfの跳ねてるのチェック # futures = [] - # with ThreadPoolExecutor(thread_name_prefix="twist_smooth{0}".format(data_set_idx), max_workers=self.options.max_workers) as executor: - # for bone_name in [arm_bone_name, arm_twist_bone_name, elbow_bone_name, wrist_twist_bone_name, wrist_bone_name]: - # futures.append(executor.submit(self.smooth_twist, data_set_idx, bone_name)) + # with ThreadPoolExecutor(thread_name_prefix="smooth_twist{0}".format(data_set_idx), max_workers=self.options.max_workers) as executor: + # for bone_name in [arm_twist_bone_name, wrist_twist_bone_name]: + # futures.append(executor.submit(self.smooth_twist, self, data_set_idx, bone_name)) # concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) # for f in futures: # if not f.result(): # return False - logger.info("%s捩り分散後処理 - フィルタリング【No.%s】", arm_bone_name, (data_set_idx + 1)) + # logger.info("%s捩り分散後処理 - フィルタリング【No.%s】", arm_bone_name, (data_set_idx + 1)) - # 捩りボーンのbfにフィルターをかける - futures = [] - with ThreadPoolExecutor(thread_name_prefix="twist_smooth_twist{0}".format(data_set_idx), max_workers=self.options.max_workers) as executor: - for bone_name in [arm_twist_bone_name, wrist_twist_bone_name]: - futures.append(executor.submit(self.smooth_filter_twist, data_set_idx, bone_name, \ - config={"freq": 30, "mincutoff": 0.01, "beta": 0.05, "dcutoff": 0.5})) + # # 捩りボーンのbfの跳ねてるのチェック + # futures = [] + # with ThreadPoolExecutor(thread_name_prefix="twist_smooth_twist{0}".format(data_set_idx), max_workers=self.options.max_workers) as executor: + # for bone_name in [arm_twist_bone_name, wrist_twist_bone_name]: + # futures.append(executor.submit(self.smooth_filter_twist, self, data_set_idx, bone_name, \ + # config={"freq": 30, "mincutoff": 0.01, "beta": 0.05, "dcutoff": 0.5})) - concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) - for f in futures: - if not f.result(): - return False + # concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) + # for f in futures: + # if not f.result(): + # return False - # 各ボーンのbfにフィルターをかける - futures = [] - with ThreadPoolExecutor(thread_name_prefix="twist_smooth{0}".format(data_set_idx), max_workers=self.options.max_workers) as executor: - for bone_name in [arm_bone_name, elbow_bone_name, wrist_bone_name, arm_twist_bone_name, wrist_twist_bone_name]: - futures.append(executor.submit(self.smooth_filter_twist, data_set_idx, bone_name, \ - config={"freq": 30, "mincutoff": 0.03, "beta": 0.1, "dcutoff": 1})) + # # 捩りボーンのbfにフィルターをかける + # futures = [] + # with ThreadPoolExecutor(thread_name_prefix="twist_smooth_twist{0}".format(data_set_idx), max_workers=self.options.max_workers) as executor: + # for bone_name in [arm_twist_bone_name, wrist_twist_bone_name]: + # futures.append(executor.submit(self.smooth_filter_twist, self, data_set_idx, bone_name, \ + # config={"freq": 30, "mincutoff": 0.01, "beta": 0.05, "dcutoff": 0.5})) - concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) - for f in futures: - if not f.result(): - return False + # concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) + # for f in futures: + # if not f.result(): + # return False + + # # 各ボーンのbfにフィルターをかける + # futures = [] + # with ThreadPoolExecutor(thread_name_prefix="twist_smooth{0}".format(data_set_idx), max_workers=self.options.max_workers) as executor: + # for bone_name in [arm_bone_name, elbow_bone_name, wrist_bone_name, arm_twist_bone_name, wrist_twist_bone_name]: + # futures.append(executor.submit(self.smooth_filter_twist, self, data_set_idx, bone_name, \ + # config={"freq": 30, "mincutoff": 0.03, "beta": 0.1, "dcutoff": 1})) + + # concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) + # for f in futures: + # if not f.result(): + # return False logger.info("%s捩り分散:終了【No.%s】", direction, (data_set_idx + 1)) else: @@ -279,7 +432,9 @@ def spread_twist_lr(self, data_set_idx: int, direction: str): logger.error("サイジング処理が意図せぬエラーで終了しました。\n\n%s", traceback.print_exc()) raise e - def remove_unnecessary_bf_pool_parts(self, data_set_idx: int, bone_name: str, offset: int): + cdef bint remove_unnecessary_bf_pool_parts(self, int data_set_idx, str bone_name, int offset): + cdef MOptionsDataSet data_set + try: logger.copy(self.options) data_set = self.options.data_set_list[data_set_idx] @@ -297,19 +452,41 @@ def remove_unnecessary_bf_pool_parts(self, data_set_idx: int, bone_name: str, of logger.error("サイジング処理が意図せぬエラーで終了しました。\n\n%s", traceback.print_exc()) raise e - def regist_twist_bf(self, data_set_idx: int, bone_name: str, fnos: list): + cdef bint regist_twist_bf(self, int data_set_idx, str bone_name, list fnos, str parent_bone_name): + cdef MOptionsDataSet data_set + cdef int prev_sep_fno, fno + cdef VmdBoneFrame bf, parent_bf + cdef list target_fnos + cdef bint is_target_copy = (parent_bone_name is not None) + try: logger.copy(self.options) data_set = self.options.data_set_list[data_set_idx] - + prev_sep_fno = 0 for fno in fnos: + # 一旦補間曲線は弄らないでそのままコピーする bf = data_set.motion.calc_bf(bone_name, fno) data_set.motion.regist_bf(bf, bone_name, fno) - if fno // 500 > prev_sep_fno and fnos[-1] > 0: + if fno // 2000 > prev_sep_fno and fnos[-1] > 0: logger.info("-- %sフレーム目:終了(%s%)【No.%s - キーフレ追加 - %s】", fno, round((fno / fnos[-1]) * 100, 3), data_set_idx + 1, bone_name) - prev_sep_fno = fno // 500 + prev_sep_fno = fno // 2000 + + if is_target_copy: + prev_sep_fno = 0 + for fno in fnos: + bf = data_set.motion.calc_bf(bone_name, fno) + + if not bf.read and bf.rotation.toDegree() < 3: + # 読み込みキーではなく、かつ角度が非常に小さい場合、親ボーンの補間曲線をコピーする + parent_bf = data_set.motion.calc_bf(parent_bone_name, fno).copy() + data_set.motion.copy_interpolation(parent_bf, bf, MBezierUtils.BZ_TYPE_R) + data_set.motion.regist_bf(bf, bone_name, fno, copy_interpolation=True) + + if fno // 2000 > prev_sep_fno and fnos[-1] > 0: + logger.info("-- %sフレーム目:終了(%s%)【No.%s - 補間曲線設定 - %s】", fno, round((fno / fnos[-1]) * 100, 3), data_set_idx + 1, bone_name) + prev_sep_fno = fno // 2000 return True except MKilledException as ke: @@ -321,14 +498,16 @@ def regist_twist_bf(self, data_set_idx: int, bone_name: str, fnos: list): import traceback logger.error("サイジング処理が意図せぬエラーで終了しました。\n\n%s", traceback.print_exc()) raise e - - def smooth_twist(self, data_set_idx: int, bone_name: str): + + cdef bint smooth_twist(self, int data_set_idx, str bone_name): + cdef MOptionsDataSet data_set + try: logger.copy(self.options) data_set = self.options.data_set_list[data_set_idx] data_set.motion.smooth_bf(data_set_idx + 1, bone_name, data_set.rep_model.bones[bone_name].getRotatable(), \ - data_set.rep_model.bones[bone_name].getTranslatable(), limit_degrees=1) + data_set.rep_model.bones[bone_name].getTranslatable(), limit_degrees=2) return True except MKilledException as ke: @@ -340,8 +519,10 @@ def smooth_twist(self, data_set_idx: int, bone_name: str): import traceback logger.error("サイジング処理が意図せぬエラーで終了しました。\n\n%s", traceback.print_exc()) raise e + + cdef bint smooth_filter_twist(self, int data_set_idx, str bone_name, dict config): + cdef MOptionsDataSet data_set - def smooth_filter_twist(self, data_set_idx: int, bone_name: str, config: dict): try: logger.copy(self.options) data_set = self.options.data_set_list[data_set_idx] @@ -360,53 +541,114 @@ def smooth_filter_twist(self, data_set_idx: int, bone_name: str, config: dict): logger.error("サイジング処理が意図せぬエラーで終了しました。\n\n%s", traceback.print_exc()) raise e - # 捩り分散後のチェック処理 - def check_twist_pool(self, data_set_idx: int, fno_idx: int, fno: int, last_fno: int, arm_bone_name: str, arm_twist_bone_name: str, elbow_bone_name: str, \ - wrist_twist_bone_name: str, wrist_bone_name: str, arm_local_x_axis: str, arm_twist_local_x_axis: MVector3D, elbow_local_x_axis: MVector3D, \ - elbow_local_y_axis: MVector3D, wrist_twist_local_x_axis: MVector3D, wrist_local_x_axis: MVector3D, wrist_local_y_axis: MVector3D, log_target_idxs: list): + # 分散後のフリップチェック + cdef bint check_twist_pool(self, int data_set_idx, VmdMotion prev_twist_motion, int fno_idx, int fno, int last_fno, str arm_bone_name, str arm_twist_bone_name, str elbow_bone_name, \ + str wrist_twist_bone_name, str wrist_bone_name, MVector3D arm_local_x_axis, MVector3D arm_local_y_axis, MVector3D arm_twist_local_x_axis, \ + MVector3D arm_twist_local_y_axis, MVector3D elbow_local_x_axis, MVector3D elbow_local_y_axis, \ + MVector3D wrist_twist_local_x_axis, MVector3D wrist_twist_local_y_axis, MVector3D wrist_local_x_axis, MVector3D wrist_local_y_axis, \ + MQuaternion elbow_y2z_qq, MVector3D elbow_local_z2y_axis, double elbow_stance_degree, list log_target_idxs, str count): + + cdef MOptionsDataSet data_set + cdef VmdBoneFrame org_arm_bf, org_arm_twist_bf, org_elbow_bf, org_wrist_twist_bf, org_wrist_bf, arm_bf, arm_twist_bf, elbow_bf, wrist_twist_bf, wrist_bf + cdef MMatrix4x4 original_mat, original_origin_mat, twisted_mat + try: logger.copy(self.options) data_set = self.options.data_set_list[data_set_idx] - # 削除後(強制補間曲線再計算後)の値 - arm_twist_bf = data_set.motion.calc_bf(arm_twist_bone_name, fno) - if not arm_twist_bf.key: - # 有効な前後のキー - prev_arm_twist_fno, next_arm_twist_fno = data_set.motion.get_bone_prev_next_fno(arm_twist_bone_name, fno, is_key=True) - # 前後のキーフレ - prev_arm_twist_bf = data_set.motion.calc_bf(arm_twist_bone_name, prev_arm_twist_fno) - next_arm_twist_bf = data_set.motion.calc_bf(arm_twist_bone_name, next_arm_twist_fno) - # 補間曲線を元に間を埋める - now_arm_twist_qq = data_set.motion.calc_bf_rot(prev_arm_twist_bf, arm_twist_bf, next_arm_twist_bf) - else: - now_arm_twist_qq = arm_twist_bf.rotation - - # 削除前後の差 - arm_twist_cancel_qq = now_arm_twist_qq.inverted() * arm_twist_bf.org_rotation + logger.test("f: %s start -------------", fno) - # 手捩りの削除後(強制補間曲線再計算後)の値 + # 各ボーンのbf + arm_bf = data_set.motion.calc_bf(arm_bone_name, fno) + arm_twist_bf = data_set.motion.calc_bf(arm_twist_bone_name, fno) + elbow_bf = data_set.motion.calc_bf(elbow_bone_name, fno) wrist_twist_bf = data_set.motion.calc_bf(wrist_twist_bone_name, fno) - if not wrist_twist_bf.key: - # 有効な前後のキー - prev_wrist_twist_fno, next_wrist_twist_fno = data_set.motion.get_bone_prev_next_fno(wrist_twist_bone_name, fno, is_key=True) - # 前後のキーフレ - prev_wrist_twist_bf = data_set.motion.calc_bf(wrist_twist_bone_name, prev_wrist_twist_fno) - next_wrist_twist_bf = data_set.motion.calc_bf(wrist_twist_bone_name, next_wrist_twist_fno) - # 補間曲線を元に間を埋める - now_wrist_twist_qq = data_set.motion.calc_bf_rot(prev_wrist_twist_bf, wrist_twist_bf, next_wrist_twist_bf) - else: - now_wrist_twist_qq = wrist_twist_bf.rotation + wrist_bf = data_set.motion.calc_bf(wrist_bone_name, fno) - # 腕捩りのキャンセルを行った手捩りの回転 - wrist_recancel_qq = arm_twist_cancel_qq * now_wrist_twist_qq + # 分散前ボーンのbf + org_arm_bf = prev_twist_motion.calc_bf(arm_bone_name, fno) + org_arm_twist_bf = prev_twist_motion.calc_bf(arm_twist_bone_name, fno) + org_elbow_bf = prev_twist_motion.calc_bf(elbow_bone_name, fno) + org_wrist_twist_bf = prev_twist_motion.calc_bf(wrist_twist_bone_name, fno) + org_wrist_bf = prev_twist_motion.calc_bf(wrist_bone_name, fno) + + # オリジナル + original_mat = MMatrix4x4() + original_mat.setToIdentity() + original_mat.rotate(org_arm_bf.rotation) + original_mat.translate(arm_local_x_axis) + original_mat.rotate(org_arm_twist_bf.rotation) + original_mat.translate(arm_twist_local_x_axis) + original_mat.rotate(org_elbow_bf.rotation) + original_mat.translate(elbow_local_x_axis) + original_mat.rotate(org_wrist_twist_bf.rotation) + original_mat.translate(wrist_twist_local_x_axis) + original_mat.rotate(org_wrist_bf.rotation) + + # オリジナルの手首方向 + original_x_vec = original_mat * wrist_local_x_axis + original_y_vec = original_mat * wrist_local_y_axis + + # オリジナルから見たオリジナルの手首方向 + original_local_x_vec = original_mat.inverted() * original_x_vec + original_local_y_vec = original_mat.inverted() * original_y_vec + + # 捩り分散後 + twisted_mat = MMatrix4x4() + twisted_mat.setToIdentity() + twisted_mat.rotate(arm_bf.rotation) + twisted_mat.translate(arm_local_x_axis) + twisted_mat.rotate(arm_twist_bf.rotation) + twisted_mat.translate(arm_twist_local_x_axis) + twisted_mat.rotate(elbow_bf.rotation) + twisted_mat.translate(elbow_local_x_axis) + twisted_mat.rotate(wrist_twist_bf.rotation) + twisted_mat.translate(wrist_twist_local_x_axis) + twisted_mat.rotate(wrist_bf.rotation) + + # 捩り分散後の手首方向 + twisted_x_vec = twisted_mat * wrist_local_x_axis + twisted_y_vec = twisted_mat * wrist_local_y_axis + + # オリジナルから見た捩り分散後の手首方向 + twisted_local_x_vec = original_mat.inverted() * twisted_x_vec + twisted_local_y_vec = original_mat.inverted() * twisted_y_vec + + # オリジナルと分散後の差 + twist_test_y_dot = MVector3D.dotProduct(twisted_local_y_vec.normalized(), original_local_y_vec.normalized()) + twist_test_x_dot = MVector3D.dotProduct(twisted_local_x_vec.normalized(), original_local_x_vec.normalized()) + twist_test_dot = np.mean([twist_test_x_dot, twist_test_y_dot]) + + if 0.95 > twist_test_dot: + # 離れていたらやり直し + logger.debug("×中間乖離%s f: %s, %s, twist_test_dot: %s, twist_test_x_dot: %s, twist_test_y_dot: %s", count, fno, arm_twist_bone_name, twist_test_dot, twist_test_x_dot, twist_test_y_dot) + + arm_bf.rotation = org_arm_bf.rotation + data_set.motion.c_regist_bf(arm_bf, arm_bone_name, fno, copy_interpolation=False) + + arm_twist_bf.rotation = org_arm_twist_bf.rotation + data_set.motion.c_regist_bf(arm_twist_bf, arm_twist_bone_name, fno, copy_interpolation=False) + + elbow_bf.rotation = org_elbow_bf.rotation + data_set.motion.c_regist_bf(elbow_bf, elbow_bone_name, fno, copy_interpolation=False) + + wrist_twist_bf.rotation = org_wrist_twist_bf.rotation + data_set.motion.c_regist_bf(wrist_twist_bf, wrist_twist_bone_name, fno, copy_interpolation=False) + + wrist_bf.rotation = org_wrist_bf.rotation + data_set.motion.c_regist_bf(wrist_bf, wrist_bone_name, fno, copy_interpolation=False) - logger.debug("再チェック f: %s, %s: 腕捩: 前: %s, 後: %s, 差: %s, 手捩: 前: %s, 後: %s, %s", fno, arm_twist_bone_name, arm_twist_bf.org_rotation.toDegree(), \ - now_arm_twist_qq.toDegree(), arm_twist_cancel_qq.toDegree(), now_wrist_twist_qq.toDegree(), wrist_recancel_qq.toDegree(), wrist_recancel_qq) + self.spread_twist_pool(data_set_idx, fno_idx, fno, last_fno, \ + arm_bone_name, arm_twist_bone_name, elbow_bone_name, wrist_twist_bone_name, wrist_bone_name, \ + arm_local_x_axis, arm_local_y_axis, arm_twist_local_x_axis, arm_twist_local_y_axis, elbow_local_x_axis, elbow_local_y_axis, \ + wrist_twist_local_x_axis, wrist_twist_local_y_axis, wrist_local_x_axis, wrist_local_y_axis, \ + elbow_y2z_qq, elbow_local_z2y_axis, elbow_stance_degree, log_target_idxs) - # 軸に基いた角度で再計算 - wrist_twist_bf.rotation = MQuaternion.fromAxisAndAngle(wrist_twist_local_x_axis, wrist_recancel_qq.toDegree() * (np.sign(wrist_recancel_qq.x() * -1))) - wrist_twist_bf.key = True - data_set.motion.bones[wrist_twist_bone_name][fno] = wrist_twist_bf + else: + logger.debug("○中間一致 f: %s, %s, twist_test_dot: %s, twist_test_x_dot: %s, twist_test_y_dot: %s", fno, arm_twist_bone_name, twist_test_dot, twist_test_x_dot, twist_test_y_dot) + + if fno in log_target_idxs and last_fno > 0: + logger.info("-- %sフレーム目【No.%s - 中間捩り分散%s - %s】", fno, data_set_idx + 1, count, arm_twist_bone_name) return True except MKilledException as ke: @@ -420,9 +662,18 @@ def check_twist_pool(self, data_set_idx: int, fno_idx: int, fno: int, last_fno: raise e # 捩り分散のPool内処理 - def spread_twist_pool(self, data_set_idx: int, fno_idx: int, fno: int, last_fno: int, arm_bone_name: str, arm_twist_bone_name: str, elbow_bone_name: str, \ - wrist_twist_bone_name: str, wrist_bone_name: str, arm_local_x_axis: str, arm_twist_local_x_axis: MVector3D, elbow_local_x_axis: MVector3D, \ - elbow_local_y_axis: MVector3D, wrist_twist_local_x_axis: MVector3D, wrist_local_x_axis: MVector3D, wrist_local_y_axis: MVector3D, log_target_idxs: list): + cdef bint spread_twist_pool(self, int data_set_idx, int fno_idx, int fno, int last_fno, str arm_bone_name, str arm_twist_bone_name, str elbow_bone_name, \ + str wrist_twist_bone_name, str wrist_bone_name, MVector3D arm_local_x_axis, MVector3D arm_local_y_axis, MVector3D arm_twist_local_x_axis, \ + MVector3D arm_twist_local_y_axis, MVector3D elbow_local_x_axis, MVector3D elbow_local_y_axis, \ + MVector3D wrist_twist_local_x_axis, MVector3D wrist_twist_local_y_axis, MVector3D wrist_local_x_axis, MVector3D wrist_local_y_axis, \ + MQuaternion elbow_y2z_qq, MVector3D elbow_local_z2y_axis, double elbow_stance_degree, list log_target_idxs): + + cdef MOptionsDataSet data_set + cdef VmdBoneFrame arm_bf, arm_twist_bf, elbow_bf, wrist_twist_bf, wrist_bf + cdef MQuaternion arm_x_qq, arm_y_qq, arm_z_qq, arm_yz_qq, elbow_x_qq, elbow_y_qq, elbow_z_qq, elbow_yz_qq, wrist_x_qq, wrist_y_qq, wrist_z_qq, wrist_yz_qq + cdef MQuaternion arm_result_qq, elbow_result_qq, arm_x_twisted_qq, arm_twist_result_qq, wrist_result_qq, wrist_x_twisted_qq, wrist_twist_result_qq + cdef double arm_twist_result_dot, wrist_twist_result_dot, wrist_result_dot, arm_x_twisted_degree, elbow_result_degree, elbow_result_dot + try: logger.copy(self.options) data_set = self.options.data_set_list[data_set_idx] @@ -437,64 +688,53 @@ def spread_twist_pool(self, data_set_idx: int, fno_idx: int, fno: int, last_fno: wrist_bf = data_set.motion.calc_bf(wrist_bone_name, fno) # 回転をローカル軸で分離 - arm_x_qq, arm_y_qq, arm_z_qq, arm_yz_qq = MServiceUtils.separate_local_qq(fno, arm_bone_name, arm_bf.rotation, arm_local_x_axis) + arm_x_qq, arm_y_qq, arm_z_qq, arm_yz_qq = MServiceUtils.separate_local_qq(fno, arm_bone_name, arm_bf.rotation, arm_twist_local_x_axis) elbow_x_qq, elbow_y_qq, elbow_z_qq, elbow_yz_qq = MServiceUtils.separate_local_qq(fno, elbow_bone_name, elbow_bf.rotation, elbow_local_x_axis) - wrist_x_qq, wrist_y_qq, wrist_z_qq, wrist_yz_qq = MServiceUtils.separate_local_qq(fno, wrist_bone_name, wrist_bf.rotation, wrist_local_x_axis) + wrist_x_qq, wrist_y_qq, wrist_z_qq, wrist_yz_qq = MServiceUtils.separate_local_qq(fno, wrist_bone_name, wrist_bf.rotation, wrist_twist_local_x_axis) logger.test("f: %s, %s: total: %s", fno, arm_bone_name, arm_bf.rotation.toEulerAngles()) logger.test("f: %s, %s: x: %s", fno, arm_bone_name, arm_x_qq.toEulerAngles()) logger.test("f: %s, %s: y: %s", fno, arm_bone_name, arm_y_qq.toEulerAngles()) logger.test("f: %s, %s: z: %s", fno, arm_bone_name, arm_z_qq.toEulerAngles()) logger.test("f: %s, %s: yz: %s", fno, arm_bone_name, arm_yz_qq.toEulerAngles()) - logger.test("f: %s, %s: total: %s", fno, elbow_bone_name, elbow_bf.rotation.toEulerAngles()) - logger.test("f: %s, %s: x: %s", fno, elbow_bone_name, elbow_x_qq.toEulerAngles()) - logger.test("f: %s, %s: y: %s", fno, elbow_bone_name, elbow_y_qq.toEulerAngles()) - logger.test("f: %s, %s: z: %s", fno, elbow_bone_name, elbow_z_qq.toEulerAngles()) - logger.test("f: %s, %s: yz: %s", fno, elbow_bone_name, elbow_yz_qq.toEulerAngles()) + logger.debug("f: %s, %s: total: %s, x: %s, y: %s, z: %s, yz: %s", fno, elbow_bone_name, elbow_bf.rotation.toDegree(), elbow_x_qq.toDegree(), elbow_y_qq.toDegree(), elbow_z_qq.toDegree(), elbow_yz_qq) logger.test("f: %s, %s: total: %s", fno, wrist_bone_name, wrist_bf.rotation.toEulerAngles()) logger.test("f: %s, %s: x: %s", fno, wrist_bone_name, wrist_x_qq.toEulerAngles()) logger.test("f: %s, %s: y: %s", fno, wrist_bone_name, wrist_y_qq.toEulerAngles()) logger.test("f: %s, %s: z: %s", fno, wrist_bone_name, wrist_z_qq.toEulerAngles()) logger.test("f: %s, %s: yz: %s", fno, wrist_bone_name, wrist_yz_qq.toEulerAngles()) - # 腕YZを腕に - arm_result_qq = arm_yz_qq + # 腕Xを腕捩りに + arm_twist_degree = arm_x_qq.toDegree() * np.sign(MVector3D.dotProduct(arm_twist_local_x_axis, arm_x_qq.vector())) - # 通常はひじYZ回転をひじボーンの順回転として扱う - # FIXME 逆肘考慮 - elbow_result_qq = MQuaternion.fromAxisAndQuaternion(elbow_local_y_axis, elbow_yz_qq) - - # 腕捩り ------------------------- - - # 腕Xを腕捩りに適用させる - arm_x_twisted_qq = MQuaternion.fromAxisAndQuaternion(arm_twist_local_x_axis, arm_x_qq) - - # 腕捩りの回転量を取得する - arm_twist_result_dot, arm_twist_result_qq = self.calc_twist_qq(data_set_idx, fno, arm_twist_bone_name, None, None, None, None, None, \ - arm_local_x_axis, arm_bf.rotation, arm_result_qq, \ - arm_twist_local_x_axis, arm_twist_bf.rotation, arm_x_twisted_qq, \ - elbow_local_x_axis, elbow_local_y_axis, elbow_bf.rotation, elbow_result_qq) - logger.debug("f: %s, %s: 腕捩り: dot: %s, degree: %s, %s", fno, arm_twist_bone_name, arm_twist_result_dot, arm_twist_result_qq.toDegree(), arm_twist_result_qq) - - # 手首YZ回転を手首に - wrist_result_qq = wrist_bf.rotation + # ひじYZをひじに + if elbow_y_qq.toDegree() + 3 < elbow_z_qq.toDegree(): + # ZひじはZとの内積 + elbow_degree = elbow_yz_qq.toDegree() * np.sign(MVector3D.dotProduct(MVector3D(0, 0, -1 * np.sign(elbow_local_x_axis.x())), elbow_yz_qq.vector())) + else: + # YひじはYとの内積 + elbow_degree = elbow_yz_qq.toDegree() * np.sign(MVector3D.dotProduct(elbow_local_y_axis, elbow_yz_qq.vector())) - # ひじXを手捻りに - wrist_x_twisted_qq = MQuaternion.fromAxisAndQuaternion(wrist_twist_local_x_axis, elbow_x_qq) - logger.test("f: %s, %s: ひじX: %s", fno, wrist_twist_bone_name, wrist_x_twisted_qq) + # 腕~ひじまでを求める + (arm_result_dot, arm_result_qq, arm_twist_result_dot, arm_twist_result_qq, elbow_result_dot, elbow_result_qq) \ + = self.calc_arm_twist_elbow_qq(data_set_idx, fno, arm_bone_name, arm_local_x_axis, arm_local_y_axis, arm_bf.rotation, arm_yz_qq, \ + arm_twist_bone_name, arm_twist_local_x_axis, arm_twist_local_y_axis, arm_twist_bf.rotation, arm_twist_degree, \ + elbow_bone_name, elbow_local_x_axis, elbow_local_y_axis, MVector3D(0, 0, -1), elbow_y2z_qq, elbow_bf.rotation, elbow_degree, elbow_y_qq, elbow_z_qq, \ + wrist_twist_bone_name, wrist_twist_local_x_axis, wrist_twist_local_y_axis, wrist_twist_bf.rotation, \ + wrist_bone_name, wrist_local_x_axis, wrist_local_y_axis, wrist_bf.rotation, elbow_stance_degree) # 手首Xを手捻りに - wrist_x_twisted_qq *= MQuaternion.fromAxisAndQuaternion(wrist_twist_local_x_axis, wrist_x_qq) - logger.test("f: %s, %s: 手首X: %s", fno, wrist_twist_bone_name, wrist_x_twisted_qq) - - # 手捩りの回転量を取得する - wrist_twist_result_dot, wrist_twist_result_qq = self.calc_twist_qq(data_set_idx, fno, wrist_twist_bone_name, arm_local_x_axis, arm_bf.rotation, arm_result_qq, \ - arm_twist_bf.rotation, arm_twist_result_qq, \ - elbow_local_x_axis, elbow_bf.rotation, elbow_result_qq, \ - wrist_twist_local_x_axis, wrist_twist_bf.rotation, wrist_x_twisted_qq, \ - wrist_local_x_axis, wrist_local_y_axis, wrist_bf.rotation, wrist_result_qq) - logger.debug("f: %s, %s: 手捩り: dot: %s, degree: %s, %s", fno, wrist_twist_bone_name, wrist_twist_result_dot, wrist_twist_result_qq.toDegree(), wrist_twist_result_qq) - + wrist_twist_degree = wrist_twist_bf.rotation.toDegree() * np.sign(MVector3D.dotProduct(wrist_twist_local_x_axis, wrist_twist_bf.rotation.vector())) + wrist_twist_degree += wrist_x_qq.toDegree() * np.sign(MVector3D.dotProduct(wrist_local_x_axis, wrist_x_qq.vector())) + + # 手捩り~手首までを求める + (wrist_twist_result_dot, wrist_twist_result_qq, wrist_result_dot, wrist_result_qq) \ + = self.calc_wrist_twist_elbow_qq(data_set_idx, fno, arm_bone_name, arm_local_x_axis, arm_local_y_axis, arm_bf.rotation, arm_result_qq, \ + arm_twist_bone_name, arm_twist_local_x_axis, arm_twist_local_y_axis, arm_twist_bf.rotation, arm_twist_result_qq, \ + elbow_bone_name, elbow_local_x_axis, elbow_local_y_axis, elbow_bf.rotation, elbow_result_qq, \ + wrist_twist_bone_name, wrist_twist_local_x_axis, wrist_twist_local_y_axis, wrist_twist_bf.rotation, wrist_twist_degree, \ + wrist_bone_name, wrist_local_x_axis, wrist_local_y_axis, wrist_bf.rotation, wrist_yz_qq) + # 全て登録 arm_bf.rotation = arm_result_qq arm_bf.key = True @@ -532,251 +772,662 @@ def spread_twist_pool(self, data_set_idx: int, fno_idx: int, fno: int, last_fno: logger.error("サイジング処理が意図せぬエラーで終了しました。\n\n%s", traceback.print_exc()) raise e - # 捩りの回転量を計算する - def calc_twist_qq(self, data_set_idx: int, fno: int, bone_name: str, grand_parent_x_axis: MVector3D, original_grand_parent_qq: MQuaternion, \ - grand_parent_qq: MQuaternion, original_grand_parent_twist_qq: MQuaternion, grand_parent_twist_qq: MQuaternion, \ - parent_x_axis: MVector3D, original_parent_qq: MQuaternion, parent_qq: MQuaternion, \ - twist_x_axis: MVector3D, original_twist_qq: MQuaternion, twist_qq: MQuaternion, \ - child_x_axis: MVector3D, child_y_axis: MVector3D, original_child_qq: MQuaternion, child_qq: MQuaternion): - - # ローカル座標系(ボーンベクトルが(1,0,0)になる空間)の向き - local_x_axis = MVector3D(1, 0, 0) - - # グローバル座標系(Aスタンス)からローカル座標系(ボーンベクトルが(1,0,0)になる空間)への変換 - child_global2local_qq = MQuaternion.rotationTo(child_x_axis, local_x_axis) - - # 軸制限のローカル座標に変換 - original_mat = MMatrix4x4() # オリジナルの回転量に基づく移動量 - original_mat.setToIdentity() # 初期化 - original_mat.rotate(original_child_qq) # 元々のひじ・手首の回転量 - original_mat.rotate(child_global2local_qq) # グローバルからローカルへ - original_mat.translate(local_x_axis) # ローカルX軸方向の移動量 - original_vec = original_mat * MVector3D() # ベクトル算出 - original_vec.setX(0) # Xの捩りは潰す - - separated_mat = MMatrix4x4() # 分散後の回転量に基づく移動量 - separated_mat.setToIdentity() # 初期化 - separated_mat.rotate(child_qq) # ひじ・手首の回転量 - separated_mat.rotate(child_global2local_qq) # グローバルからローカルへ - separated_mat.translate(local_x_axis) # ローカルX軸方向の移動量 - separated_vec = separated_mat * MVector3D() # ベクトル算出 - separated_vec.setX(0) # Xの捩りは潰す - - # 分散後の回転量からオリジナルの回転量に戻すだけの回転量 - local_qq = MQuaternion.rotationTo(separated_vec.normalized(), original_vec.normalized()) - - # グローバル軸に戻す - # global_qq = original_twist_qq * twist_qq * local_qq - total_degree = twist_qq.toDegree() + local_qq.toDegree() # original_twist_qq.toDegree() - total_qq = MQuaternion.fromAxisAndAngle(twist_x_axis, total_degree) - logger.debug("f: %s, 生成: %s, total_degree: %s", fno, bone_name, total_degree) - - logger.test("fno: %s, original_twist_qq: %s, %s", fno, original_twist_qq.toDegree(), original_twist_qq) - logger.test("fno: %s, twist_qq: %s, %s", fno, twist_qq.toDegree(), twist_qq) - logger.test("fno: %s, local_qq: %s, %s", fno, local_qq.toDegree(), local_qq) - # logger.test("fno: %s, global_qq: %s, %s", fno, global_qq.toDegree(), global_qq) - logger.test("fno: %s, total_qq: %s, %s", fno, total_qq.toDegree(), total_qq) - - original_mat = MMatrix4x4() # オリジナルの回転量に基づく移動量 - original_mat.setToIdentity() # 初期化 - - # 手捩りの場合、腕まで見る - if grand_parent_x_axis: - original_mat.rotate(original_grand_parent_qq) # 元々の腕の回転量 - original_mat.rotate(original_grand_parent_twist_qq) # 元々の腕捩りの回転量 - original_mat.translate(grand_parent_x_axis) # 腕のX軸方向 - - original_mat.rotate(original_parent_qq) # 元々の腕・ひじの回転量 - original_mat.rotate(original_twist_qq) # 元々の腕捩り・手捩りの回転量 - original_mat.translate(parent_x_axis) # 腕・ひじのX軸方向 - original_mat.rotate(original_child_qq) # 元々のひじ・手首の回転量 - - # 手捩りの場合、Y軸でチェック - if grand_parent_x_axis: - original_vec = original_mat * child_y_axis # 手首のY軸方向 - else: - original_vec = original_mat * child_x_axis # ひじのX軸方向 - - max_dot = 0 - max_degree = 0 - - # 捩りの計算が取れなかった場合、再計算してひじ・手首の位置を確認する - # 1度単位 - max_dot, max_degree = self.test_twist_qq(fno, bone_name, grand_parent_x_axis, grand_parent_qq, grand_parent_twist_qq, \ - parent_x_axis, parent_qq, twist_x_axis, original_twist_qq, \ - child_x_axis, child_y_axis, original_child_qq, child_qq, original_vec, \ - total_degree, max_dot, max_degree, np.asarray([(x, -x) for x in range(0, 31, 1)]).flatten()) - - if max_dot > RADIANS_2: - return max_dot, MQuaternion.fromAxisAndAngle(twist_x_axis, max_degree) - - # 30度範囲でチェック(前のチェックは破棄) - max_dot, max_degree = self.test_twist_qq(fno, bone_name, grand_parent_x_axis, grand_parent_qq, grand_parent_twist_qq, \ - parent_x_axis, parent_qq, twist_x_axis, original_twist_qq, \ - child_x_axis, child_y_axis, original_child_qq, child_qq, original_vec, \ - total_degree, max_dot, max_degree, np.asarray([(x, -x) for x in range(0, 181, 10)]).flatten()) - - if max_dot > RADIANS_2: - return max_dot, MQuaternion.fromAxisAndAngle(twist_x_axis, max_degree) - - # 1度単位 - total_degree = max_degree - max_dot, max_degree = self.test_twist_qq(fno, bone_name, grand_parent_x_axis, grand_parent_qq, grand_parent_twist_qq, \ - parent_x_axis, parent_qq, twist_x_axis, original_twist_qq, \ - child_x_axis, child_y_axis, original_child_qq, child_qq, original_vec, \ - total_degree, max_dot, max_degree, np.asarray([(x, -x) for x in range(1, 21, 1)]).flatten()) - - if max_dot > RADIANS_2: - return max_dot, MQuaternion.fromAxisAndAngle(twist_x_axis, max_degree) - - # 整数で取れなかった場合、小数まで割って調べる - total_degree = max_degree - max_dot, max_degree = self.test_twist_qq(fno, bone_name, grand_parent_x_axis, grand_parent_qq, grand_parent_twist_qq, \ - parent_x_axis, parent_qq, twist_x_axis, original_twist_qq, \ - child_x_axis, child_y_axis, original_child_qq, child_qq, original_vec, \ - total_degree, max_dot, max_degree, np.asarray([(x * 0.1, -x * 0.1) for x in range(1, 11)]).flatten()) - - if max_dot > RADIANS_8: - return max_dot, MQuaternion.fromAxisAndAngle(twist_x_axis, max_degree) - - # 最後まで近似が取れなかった場合最も近いの - logger.warning("【No.%s】%sフレーム目:%s捩り分散失敗: 角度: %s 近似度: %s", (data_set_idx + 1), fno, bone_name, round(max_degree, 3), round(max_dot, 5)) - return max_dot, MQuaternion.fromAxisAndAngle(twist_x_axis, max_degree) - - def test_twist_qq(self, fno: int, bone_name: str, grand_parent_x_axis: MVector3D, grand_parent_qq: MQuaternion, grand_parent_twist_qq: MQuaternion, \ - parent_x_axis: MVector3D, parent_qq: MQuaternion, twist_x_axis: MVector3D, original_twist_qq: MQuaternion, \ - child_x_axis: MVector3D, child_y_axis: MVector3D, original_child_qq: MQuaternion, child_qq: MQuaternion, original_vec: MVector3D, \ - total_degree: float, max_dot: float, max_degree: float, degree_range: list): - - for n, append_degree in enumerate(degree_range): - # 正+方向の捩り角度 - max_dot, max_degree = self.test_twist_qq_inner(fno, bone_name, grand_parent_x_axis, grand_parent_qq, grand_parent_twist_qq, \ - parent_x_axis, parent_qq, twist_x_axis, original_twist_qq, \ - child_x_axis, child_y_axis, original_child_qq, child_qq, original_vec, \ - 0, max_dot, max_degree, total_degree + append_degree) - - if max_dot > RADIANS_2: - # 充分に近似している場合、このまま終了 - logger.debug("f: %s, %s, 確定 max_dot: %s, total_degree: %s, append_degree: %s, test_degree: %s, (正+)", fno, bone_name, max_dot, total_degree, append_degree, max_degree) - return max_dot, max_degree - - # 正-方向の捩り角度 - max_dot, max_degree = self.test_twist_qq_inner(fno, bone_name, grand_parent_x_axis, grand_parent_qq, grand_parent_twist_qq, \ - parent_x_axis, parent_qq, twist_x_axis, original_twist_qq, \ - child_x_axis, child_y_axis, original_child_qq, child_qq, original_vec, \ - 0, max_dot, max_degree, total_degree - append_degree) - - if max_dot > RADIANS_2: - # 充分に近似している場合、このまま終了 - logger.debug("f: %s, %s, 確定 max_dot: %s, total_degree: %s, append_degree: %s, test_degree: %s, (正-)", fno, bone_name, max_dot, total_degree, append_degree, max_degree) - return max_dot, max_degree - - # 逆+方向の捩り角度 - max_dot, max_degree = self.test_twist_qq_inner(fno, bone_name, grand_parent_x_axis, grand_parent_qq, grand_parent_twist_qq, \ - parent_x_axis, parent_qq, twist_x_axis, original_twist_qq, \ - child_x_axis, child_y_axis, original_child_qq, child_qq, original_vec, \ - 0, max_dot, max_degree, -total_degree + append_degree) - - if max_dot > RADIANS_2: - # 充分に近似している場合、このまま終了 - logger.debug("f: %s, %s, 確定 max_dot: %s, total_degree: %s, append_degree: %s, test_degree: %s, (逆+)", fno, bone_name, max_dot, total_degree, append_degree, max_degree) - return max_dot, max_degree - - # 逆-方向の捩り角度 - max_dot, max_degree = self.test_twist_qq_inner(fno, bone_name, grand_parent_x_axis, grand_parent_qq, grand_parent_twist_qq, \ - parent_x_axis, parent_qq, twist_x_axis, original_twist_qq, \ - child_x_axis, child_y_axis, original_child_qq, child_qq, original_vec, \ - 0, max_dot, max_degree, -total_degree - append_degree) - - if max_dot > RADIANS_2: - # 充分に近似している場合、このまま終了 - logger.debug("f: %s, %s, 確定 max_dot: %s, total_degree: %s, append_degree: %s, test_degree: %s, (逆-)", fno, bone_name, max_dot, total_degree, append_degree, max_degree) - return max_dot, max_degree - - # 最後まで取れなければ、最大近似のを返す - return max_dot, max_degree - - # 捩りの精査内部処理 - def test_twist_qq_inner(self, fno: int, bone_name: str, grand_parent_x_axis: MVector3D, grand_parent_qq: MQuaternion, grand_parent_twist_qq: MQuaternion, \ - parent_x_axis: MVector3D, parent_qq: MQuaternion, twist_x_axis: MVector3D, original_twist_qq: MQuaternion, \ - child_x_axis: MVector3D, child_y_axis: MVector3D, original_child_qq: MQuaternion, child_qq: MQuaternion, original_vec: MVector3D, \ - total_degree: float, max_dot: float, max_degree: float, test_degree: float): - - twisted_dot, twist_degree, result_twist_qq = self.confirm_twist_qq(fno, bone_name, grand_parent_x_axis, grand_parent_qq, grand_parent_twist_qq, \ - parent_x_axis, parent_qq, twist_x_axis, original_twist_qq, test_degree, \ - child_x_axis, child_y_axis, original_child_qq, child_qq, original_vec) - logger.test("f: %s, %s, twisted_dot: %s, twist_degree: %s, result_twist_qq: %s", fno, bone_name, twisted_dot, twist_degree, result_twist_qq.toEulerAngles().to_log()) + # 腕~腕捩り~ひじを求める + cdef tuple calc_arm_twist_elbow_qq(self, int data_set_idx, int fno, str arm_bone_name, MVector3D arm_local_x_axis, MVector3D arm_local_y_axis, MQuaternion original_arm_qq, \ + MQuaternion arm_qq, str arm_twist_bone_name, MVector3D arm_twist_local_x_axis, MVector3D arm_twist_local_y_axis, \ + MQuaternion original_arm_twist_qq, double arm_twist_degree, str elbow_bone_name, MVector3D elbow_local_x_axis, \ + MVector3D elbow_local_y_axis, MVector3D elbow_local_z_axis, MQuaternion elbow_y2z_qq, MQuaternion original_elbow_qq, \ + double elbow_degree, MQuaternion elbow_y_qq, MQuaternion elbow_z_qq, \ + str wrist_twist_bone_name, MVector3D wrist_twist_local_x_axis, MVector3D wrist_twist_local_y_axis, MQuaternion original_wrist_twist_qq, \ + str wrist_bone_name, MVector3D wrist_local_x_axis, MVector3D wrist_local_y_axis, MQuaternion original_wrist_qq, double elbow_stance_degree): + + cdef int n, m, i, j + cdef double elbow_result_degree, arm_twist_test_degree, twist_test_x_dot, twist_test_y_dot, twist_test_dot, elbow_result_dot, arm_twist_result_dot, x_weight + cdef MVector3D original_arm_x_vec, original_local_arm_x_vec, separate_arm_x_vec, separate_local_arm_x_vec + cdef MVector3D original_elbow_x_vec, original_local_elbow_x_vec, separate_elbow_x_vec, separate_local_elbow_x_vec + cdef MVector3D original_elbow_y_vec, original_local_elbow_y_vec, separate_elbow_y_vec, separate_local_elbow_y_vec, now_elbow_local_y_axis + cdef MVector3D original_arm_twist_x_vec, original_local_arm_twist_x_vec, separate_arm_twist_x_vec, separate_local_arm_twist_x_vec + cdef MMatrix4x4 original_arm_mat, separate_arm_mat, original_arm_twist_mat, separate_arm_twist_mat, original_elbow_mat, separate_elbow_mat, original_elbow_mat_copy, original_arm_twist_mat_copy + cdef MQuaternion elbow_yz_qq, arm_twist_test_qq, twist_x_qq, twist_y_qq, arm_twist_result_qq + cdef list degree_list, elbow_degree_list + cdef bint is_elbow_z = elbow_y_qq.toDegree() + 3 < elbow_z_qq.toDegree() - if twisted_dot > RADIANS_2: - # 充分に近似している場合、このまま終了 - return twisted_dot, twist_degree - - # 内積が近付いていたら上書き - if max_dot < twisted_dot: - max_dot = twisted_dot - max_degree = twist_degree - - # 絶対値内積が近付いてたら反転 - if max_dot < abs(twisted_dot): - test_degree = -test_degree - twisted_dot, twist_degree, result_twist_qq = self.confirm_twist_qq(fno, bone_name, grand_parent_x_axis, grand_parent_qq, grand_parent_twist_qq, \ - parent_x_axis, parent_qq, twist_x_axis, original_twist_qq, test_degree, \ - child_x_axis, child_y_axis, original_child_qq, child_qq, original_vec) - # logger.test("fno: %s, 正+ append_degree: %s, twisted_dot: %s, result_twist_qq: %s, result_twist_qq: %s", \ - # fno, append_degree, twisted_dot, result_twist_qq.toDegree(), result_twist_qq) + cdef double arm_sign = np.sign(arm_local_x_axis.x()) + cdef double arm_result_dot = 0 + cdef double arm_twist_result_degree = 0 + cdef MQuaternion arm_result_qq = MQuaternion() + cdef MQuaternion elbow_result_qq = MQuaternion() + cdef MOptionsDataSet data_set = self.options.data_set_list[data_set_idx] + cdef double prev_arm_twist_result_dot = -9999 + cdef double prev_elbow_result_dot = -9999 + cdef double prev_elbow_result_degree = 0 + cdef double prev_arm_twist_result_degree = 0 + + # 腕 -------------- + + # オリジナル + original_arm_mat = MMatrix4x4() + original_arm_mat.setToIdentity() + original_arm_mat.rotate(original_arm_qq) + + # オリジナルの腕方向 + original_arm_x_vec = original_arm_mat * arm_local_x_axis + + # オリジナルから見たオリジナルの腕方向 + original_local_arm_x_vec = original_arm_mat.inverted() * original_arm_x_vec + + # 分散後 + separate_arm_mat = MMatrix4x4() + separate_arm_mat.setToIdentity() + separate_arm_mat.rotate(arm_qq) + + # 分散後の腕方向 + separate_arm_x_vec = separate_arm_mat * arm_local_x_axis + + # オリジナルから見た分散後の腕方向 + separate_local_arm_x_vec = original_arm_mat.inverted() * separate_arm_x_vec + + # 腕YZ + arm_result_qq = MQuaternion.rotationTo(separate_local_arm_x_vec, arm_local_x_axis) + arm_result_qq = arm_qq * arm_result_qq + + # ひじ ------------ + + # オリジナルのYZ + original_elbow_mat = MMatrix4x4() + original_elbow_mat.setToIdentity() + original_elbow_mat.rotate(original_arm_qq) + original_elbow_mat.translate(arm_local_x_axis) + original_elbow_mat.rotate(original_arm_twist_qq) + original_elbow_mat.translate(arm_twist_local_x_axis) + original_elbow_mat.rotate(original_elbow_qq) + + # オリジナルのひじ方向 + original_elbow_x_vec = original_elbow_mat * elbow_local_x_axis + original_elbow_y_vec = original_elbow_mat * elbow_local_y_axis + + # オリジナルから見たオリジナルのひじ方向 + original_local_elbow_x_vec = original_elbow_mat.inverted() * original_elbow_x_vec + original_local_elbow_y_vec = original_elbow_mat.inverted() * original_elbow_y_vec + + i = 0 + # ひじ -------------- + while i < 5: + + if i == 0: + # 初回は初期化 + logger.debug("(%s) 腕捩り初期化 f: %s, %s: arm_twist_degree: %s", i, fno, elbow_bone_name, arm_twist_degree) + arm_twist_result_qq = MQuaternion.fromAxisAndAngle(arm_twist_local_x_axis, arm_twist_degree) + + # 分散後 + separate_elbow_mat = MMatrix4x4() + separate_elbow_mat.setToIdentity() + separate_elbow_mat.rotate(arm_result_qq) + separate_elbow_mat.translate(arm_local_x_axis) + separate_elbow_mat.rotate(arm_twist_result_qq) + separate_elbow_mat.translate(arm_twist_local_x_axis) + # separate_elbow_mat.rotate(elbow_result_qq) + + # ひじZに回っている場合、ローカルY軸のチェックを変える + if is_elbow_z: + # separate_elbow_mat.rotate(elbow_y2z_qq) + if elbow_degree < 0: + logger.debug("(%s) Z逆ひじ f: %s, %s: yz: %s, y: %s, z: %s", i, fno, elbow_bone_name, elbow_degree, elbow_y_qq.toDegree(), elbow_z_qq.toDegree()) + else: + logger.debug("(%s) Z順ひじ f: %s, %s: yz: %s, y: %s, z: %s", i, fno, elbow_bone_name, elbow_degree, elbow_y_qq.toDegree(), elbow_z_qq.toDegree()) + else: + if elbow_degree < 0: + logger.debug("(%s) Y逆ひじ f: %s, %s: yz: %s, y: %s, z: %s", i, fno, elbow_bone_name, elbow_degree, elbow_y_qq.toDegree(), elbow_z_qq.toDegree()) + else: + logger.debug("(%s) Y順ひじ f: %s, %s: yz: %s, y: %s, z: %s", i, fno, elbow_bone_name, elbow_degree, elbow_y_qq.toDegree(), elbow_z_qq.toDegree()) + + # 分散後の腕方向 + separate_elbow_x_vec = separate_elbow_mat * elbow_local_x_axis + separate_elbow_y_vec = separate_elbow_mat * elbow_local_y_axis + + # オリジナルから見た分散後のひじ方向 + separate_local_elbow_x_vec = original_elbow_mat.inverted() * separate_elbow_x_vec + separate_local_elbow_y_vec = original_elbow_mat.inverted() * separate_elbow_y_vec - if twisted_dot > RADIANS_2: - # 充分に近似している場合、このまま終了 - return twisted_dot, twist_degree + # ひじYZ + twist_x_qq = MQuaternion.rotationTo(separate_local_elbow_x_vec, original_local_elbow_x_vec) + + if twist_x_qq.toDegree() > 20 or np.sign(elbow_degree) > 0: + elbow_degree_list = [twist_x_qq.toDegree()] + else: + elbow_degree_list = [twist_x_qq.toDegree(), -twist_x_qq.toDegree()] + + elbow_result_degree = 0 + for j, elbow_degree in enumerate(elbow_degree_list): + # 初期化 + arm_twist_result_dot = -999 + if i == 0: + # 初回だけ初期化 + arm_twist_result_degree = arm_twist_degree + arm_twist_result_qq = MQuaternion.fromAxisAndAngle(arm_twist_local_x_axis, arm_twist_result_degree) + + elbow_result_dot = -999 + elbow_test_qq = MQuaternion.fromAxisAndAngle(elbow_local_y_axis, elbow_degree) + + # ひじ --------------- + # 分散後 + test_elbow_mat = MMatrix4x4() + test_elbow_mat.setToIdentity() + test_elbow_mat.rotate(arm_result_qq) + test_elbow_mat.translate(arm_local_x_axis) + test_elbow_mat.rotate(arm_twist_result_qq) + test_elbow_mat.translate(arm_twist_local_x_axis) + test_elbow_mat.rotate(elbow_test_qq) + + # if is_elbow_z: + # test_elbow_mat.rotate(elbow_y2z_qq) + + # テストのひじ方向 + test_elbow_x_vec = test_elbow_mat * elbow_local_x_axis + test_elbow_y_vec = test_elbow_mat * elbow_local_y_axis + + # オリジナルから見たテストのひじ方向 + test_local_elbow_x_vec = original_elbow_mat.inverted() * test_elbow_x_vec + test_local_elbow_y_vec = original_elbow_mat.inverted() * test_elbow_y_vec + + # NewValue = (((OldValue - OldMin) * (NewMax - NewMin)) / (OldMax - OldMin)) + NewMin + x_weight = max(0.5, min(1, ((((elbow_test_qq.toDegree() - 0) * (1 - 0.5)) / (20 - 0)) + 0.5))) + + # オリジナルと分散後の差 + twist_test_x_dot = MVector3D.dotProduct(test_local_elbow_x_vec.normalized(), original_local_elbow_x_vec.normalized()) + twist_test_y_dot = MVector3D.dotProduct(test_local_elbow_y_vec.normalized(), original_local_elbow_y_vec.normalized()) + elbow_result_dot = np.average([twist_test_x_dot, twist_test_y_dot], weights=[x_weight, 1 - x_weight]) + elbow_result_degree = elbow_degree + + logger.debug("(%s-%s) f: %s, %s, x_weight: %s, elbow_result_dot: %s, twist_test_x_dot: %s, twist_test_y_dot: %s", i, j, fno, elbow_bone_name, x_weight, elbow_result_dot, twist_test_x_dot, twist_test_y_dot) + + if elbow_result_dot < prev_elbow_result_dot and twist_test_y_dot <= 0 and i > 0: + # ひじが近付いてない場合、この時点でNG + logger.debug("×ひじ却下 (%s-%s) f: %s, %s, x_weight: %s, elbow_result_dot: %s, prev_elbow_result_dot: %s", i, j, fno, elbow_bone_name, x_weight, elbow_result_dot, prev_elbow_result_dot) + + # ダメだった場合、前回のを採用し直し + arm_twist_result_dot = prev_arm_twist_result_dot + arm_twist_result_degree = prev_arm_twist_result_degree + elbow_result_dot = prev_elbow_result_dot + elbow_result_degree = prev_elbow_result_degree + + arm_twist_result_qq = MQuaternion.fromAxisAndAngle(arm_twist_local_x_axis, arm_twist_result_degree) + elbow_result_qq = MQuaternion.fromAxisAndAngle(elbow_local_y_axis, elbow_result_degree) + + elbow_result_qq = MQuaternion.fromAxisAndAngle(elbow_local_y_axis, elbow_result_degree) + + # NewValue = (((OldValue - OldMin) * (NewMax - NewMin)) / (OldMax - OldMin)) + NewMin + x_weight = max(0.5, min(1, ((((elbow_result_qq.toDegree() - 0) * (1 - 0.5)) / (20 - 0)) + 0.5))) + + logger.debug("仮設定(%s-%s) f: %s, %s, x_weight: %s, elbow_result_dot: %s, elbow_result_degree: %s", i, j, fno, elbow_bone_name, x_weight, elbow_result_dot, elbow_result_degree) + + # 腕捩り ------------------ + # 分散後 + separate_arm_twist_mat = MMatrix4x4() + separate_arm_twist_mat.setToIdentity() + separate_arm_twist_mat.rotate(arm_result_qq) + separate_arm_twist_mat.translate(arm_local_x_axis) + # separate_arm_twist_mat.rotate(arm_twist_result_qq) + separate_arm_twist_mat.translate(arm_twist_local_x_axis) + separate_arm_twist_mat.rotate(elbow_result_qq) + # separate_arm_twist_mat.translate(elbow_local_x_axis) + # separate_arm_twist_mat.rotate(original_wrist_twist_qq) + # separate_arm_twist_mat.translate(wrist_twist_local_x_axis) + # separate_arm_twist_mat.rotate(original_wrist_qq) + + # # ひじZに回っている場合、ローカルY軸のチェックを変える + # if is_elbow_z: + # separate_arm_twist_mat.rotate(elbow_y2z_qq) + + # 分散後の手首方向 + separate_arm_twist_x_vec = separate_arm_twist_mat * elbow_local_x_axis + separate_arm_twist_y_vec = separate_arm_twist_mat * elbow_local_y_axis + + # オリジナルから見た分散後の手首方向 + separate_local_arm_twist_x_vec = original_elbow_mat.inverted() * separate_arm_twist_x_vec + separate_local_arm_twist_y_vec = original_elbow_mat.inverted() * separate_arm_twist_y_vec + + # 腕捩りの初期角度 + twist_x_qq = MQuaternion.rotationTo(separate_local_arm_twist_x_vec, original_local_elbow_x_vec) + twist_y_qq = MQuaternion.rotationTo(separate_local_arm_twist_y_vec, original_local_elbow_y_vec) + + degree_list = [twist_x_qq.toDegree(), -twist_x_qq.toDegree(), twist_y_qq.toDegree(), -twist_y_qq.toDegree()] + logger.debug("候補(%s-%s) f: %s, %s, degree_list: %s, elbow: %s, x_weight: %s", i, j, fno, arm_twist_bone_name, degree_list, elbow_result_degree, x_weight) + + m = 0 + n = 0 + while m < 3 and n < 50: + for test_degree in degree_list: + arm_twist_test_degree = arm_twist_result_degree + test_degree if m > 0 else test_degree + + # if abs(arm_twist_test_degree) > 135: + # logger.debug("フリップ不可(%s-%s) f: %s, %s, n: %s(%s), arm_twist_test_degree: %s", i, j, fno, arm_twist_bone_name, n, m, arm_twist_test_degree) + # arm_twist_test_degree += (180 * np.sign(arm_twist_test_degree) * -1) + + arm_twist_test_qq = MQuaternion.fromAxisAndAngle(arm_twist_local_x_axis, arm_twist_test_degree) + + # 分散後 + test_arm_twist_mat = MMatrix4x4() + test_arm_twist_mat.setToIdentity() + test_arm_twist_mat.rotate(arm_result_qq) + test_arm_twist_mat.translate(arm_local_x_axis) + test_arm_twist_mat.rotate(arm_twist_test_qq) + test_arm_twist_mat.translate(arm_twist_local_x_axis) + test_arm_twist_mat.rotate(elbow_result_qq) + # test_arm_twist_mat.translate(elbow_local_x_axis) + # test_arm_twist_mat.rotate(original_wrist_twist_qq) + # test_arm_twist_mat.translate(wrist_twist_local_x_axis) + # test_arm_twist_mat.rotate(original_wrist_qq) + + # # ひじZに回っている場合、ローカルY軸のチェックを変える + # if is_elbow_z: + # test_arm_twist_mat.rotate(elbow_y2z_qq) + + # テストの手首方向 + test_arm_twist_x_vec = test_arm_twist_mat * elbow_local_x_axis + test_arm_twist_y_vec = test_arm_twist_mat * elbow_local_y_axis + + # オリジナルから見たテストの手首方向 + test_local_arm_twist_x_vec = original_elbow_mat.inverted() * test_arm_twist_x_vec + test_local_arm_twist_y_vec = original_elbow_mat.inverted() * test_arm_twist_y_vec + + # オリジナルと分散後の差 + twist_test_x_dot = MVector3D.dotProduct(test_local_arm_twist_x_vec.normalized(), original_local_elbow_x_vec.normalized()) + twist_test_y_dot = MVector3D.dotProduct(test_local_arm_twist_y_vec.normalized(), original_local_elbow_y_vec.normalized()) + twist_test_dot = np.average([twist_test_x_dot, twist_test_y_dot], weights=[x_weight, 1 - x_weight]) + + if twist_test_dot > arm_twist_result_dot: + # if twist_test_dot > arm_twist_result_dot and ((20 > elbow_result_qq.toDegree() and -0.5 < twist_test_y_dot) or 20 <= elbow_result_qq.toDegree()): + # より近くなった場合、角度採用 + logger.debug("〇(%s-%s) f: %s, %s, n: %s(%s), twist_test_dot: %s, twist_test_x_dot: %s, twist_test_y_dot: %s, test_degree: %s, arm_twist_result_degree: %s, arm_twist_test_degree: %s", \ + i, j, fno, arm_twist_bone_name, n, m, twist_test_dot, twist_test_x_dot, twist_test_y_dot, test_degree, arm_twist_result_degree, arm_twist_test_degree) + + arm_twist_result_dot = twist_test_dot + arm_twist_result_degree = arm_twist_test_degree + + twist_x_qq = MQuaternion.rotationTo(test_local_arm_twist_x_vec, original_local_elbow_x_vec) + # twist_y_qq = MQuaternion.rotationTo(test_local_arm_twist_y_vec, original_local_elbow_y_vec) - # 内積が近付いていたら上書き - if max_dot < twisted_dot: - max_dot = twisted_dot - max_degree = twist_degree + if m > 0: + # 1回目以降の場合、角度を取り直して再チェック + m = 0 + degree_list = [twist_x_qq.toDegree(), -twist_x_qq.toDegree(), 2, -2] + break + else: + # 近付かなかったら不採用 + logger.debug("×(%s-%s) f: %s, %s, n: %s(%s), twist_test_dot: %s, twist_test_x_dot: %s, twist_test_y_dot: %s, test_degree: %s, arm_twist_result_degree: %s, arm_twist_test_degree: %s", \ + i, j, fno, arm_twist_bone_name, n, m, twist_test_dot, twist_test_x_dot, twist_test_y_dot, test_degree, arm_twist_result_degree, arm_twist_test_degree) + + if arm_twist_result_dot > RADIANS_1: + break + + if m == 0: + # 初回は最も近付いた回転量ベースでリスト再生成 + degree_list = [twist_x_qq.toDegree(), -twist_x_qq.toDegree(), 2, -2] + elif m > 0: + # 3回目以降でダメだった場合は、量を減らして再チェック + degree_list = [d * 0.5 for d in degree_list] + + m += 1 + n += 1 + + arm_twist_result_qq = MQuaternion.fromAxisAndAngle(arm_twist_local_x_axis, arm_twist_result_degree) + logger.debug("仮設定(%s-%s) f: %s, %s, arm_twist_result_dot: %s, arm_twist_result_degree: %s", i, j, fno, arm_twist_bone_name, arm_twist_result_dot, arm_twist_result_degree) - return max_dot, max_degree + logger.debug("(%s-%s) f: %s, %s, prev: %s(%s), now: %s(%s)", i, j, fno, arm_twist_bone_name, prev_arm_twist_result_degree, prev_arm_twist_result_dot, arm_twist_result_degree, arm_twist_result_dot) + logger.debug("(%s-%s) f: %s, %s, prev: %s(%s), now: %s(%s)", i, j, fno, elbow_bone_name, prev_elbow_result_degree, prev_elbow_result_dot, elbow_result_degree, elbow_result_dot) - def confirm_twist_qq(self, fno: int, bone_name: str, grand_parent_x_axis: MVector3D, grand_parent_qq: MQuaternion, grand_parent_twist_qq: MQuaternion, \ - parent_x_axis: MVector3D, parent_qq: MQuaternion, twist_x_axis: MVector3D, original_twist_qq: MQuaternion, twist_degree: float, \ - child_x_axis: MVector3D, child_y_axis: MVector3D, original_child_qq: MQuaternion, child_qq: MQuaternion, original_vec: MVector3D): - # twist_degree = twist_degree % (180 if np.sign(twist_x_axis.x()) == 1 else -180) + if RADIANS_2 < arm_twist_result_dot and RADIANS_2 < elbow_result_dot and prev_arm_twist_result_dot < arm_twist_result_dot and prev_elbow_result_dot < elbow_result_dot: + # 充分に近い場合、終了 + break + + if prev_arm_twist_result_dot > arm_twist_result_dot or (np.sign(elbow_result_degree) != np.sign(prev_elbow_result_degree) and prev_elbow_result_dot > elbow_result_dot): + logger.debug("(%s-%s) f: %s, %s, 前回再採用", i, j, fno, arm_twist_bone_name) + # ダメだった場合、前回のを採用し直し + arm_twist_result_dot = prev_arm_twist_result_dot + arm_twist_result_degree = prev_arm_twist_result_degree + elbow_result_dot = prev_elbow_result_dot + elbow_result_degree = prev_elbow_result_degree + + arm_twist_result_qq = MQuaternion.fromAxisAndAngle(arm_twist_local_x_axis, arm_twist_result_degree) + elbow_result_qq = MQuaternion.fromAxisAndAngle(elbow_local_y_axis, elbow_result_degree) + + # 前回分として次を実行する + prev_arm_twist_result_dot = arm_twist_result_dot + prev_arm_twist_result_degree = arm_twist_result_degree + prev_elbow_result_dot = elbow_result_dot + prev_elbow_result_degree = elbow_result_degree + + if RADIANS_5 < arm_twist_result_dot and RADIANS_5 < elbow_result_dot: + # 充分に近い場合、終了 + break + + i += 1 + + # 確定 + elbow_result_qq = MQuaternion.fromAxisAndAngle(elbow_local_y_axis, elbow_result_degree) + arm_twist_result_qq = MQuaternion.fromAxisAndAngle(arm_twist_local_x_axis, arm_twist_result_degree) + + if arm_twist_result_dot < 0.9: + logger.warning("【No.%s】%sフレーム目:%s 捩り分散失敗: 角度: %s 近似度: %s", (data_set_idx + 1), fno, arm_twist_bone_name, round(arm_twist_result_degree, 3), round(arm_twist_result_dot, 5)) + + logger.debug("f: %s, %s: %s, arm_result_qq: %s(%s)", fno, arm_bone_name, arm_result_dot, arm_result_qq.toDegree(), arm_result_qq) + logger.debug("f: %s, %s: %s, arm_twist_result_qq: %s(%s)", fno, arm_twist_bone_name, arm_twist_result_dot, arm_twist_result_degree, arm_twist_result_qq) + logger.debug("f: %s, %s: %s, elbow_result_qq: %s(%s)", fno, elbow_bone_name, elbow_result_dot, elbow_result_degree, elbow_result_qq) + + return (arm_result_dot, arm_result_qq, arm_twist_result_dot, arm_twist_result_qq, elbow_result_dot, elbow_result_qq) + + cdef tuple calc_wrist_twist_elbow_qq(self, int data_set_idx, int fno, str arm_bone_name, MVector3D arm_local_x_axis, MVector3D arm_local_y_axis, MQuaternion original_arm_qq, MQuaternion arm_qq, \ + str arm_twist_bone_name, MVector3D arm_twist_local_x_axis, MVector3D arm_twist_local_y_axis, MQuaternion original_arm_twist_qq, MQuaternion arm_twist_qq, \ + str elbow_bone_name, MVector3D elbow_local_x_axis, MVector3D elbow_local_y_axis, MQuaternion original_elbow_qq, MQuaternion elbow_qq, \ + str wrist_twist_bone_name, MVector3D wrist_twist_local_x_axis, MVector3D wrist_twist_local_y_axis, MQuaternion original_wrist_twist_qq, double wrist_twist_degree, \ + str wrist_bone_name, MVector3D wrist_local_x_axis, MVector3D wrist_local_y_axis, MQuaternion original_wrist_qq, MQuaternion wrist_qq): + + cdef int i, j, n, m + cdef MMatrix4x4 original_wrist_mat, separate_wrist_mat, test_wrist_mat, separate_wrist_twist_mat, test_wrist_twist_mat + cdef MVector3D original_wrist_x_vec, original_wrist_y_vec, original_local_wrist_x_vec, original_local_wrist_y_vec, original_wrist_origin_vec + cdef MVector3D separate_wrist_x_vec, separate_wrist_y_vec, separate_local_wrist_x_vec, separate_local_wrist_y_vec, separate_local_wrist_origin_vec + cdef MVector3D test_wrist_x_vec, test_wrist_y_vec, test_local_wrist_x_vec, test_local_wrist_y_vec, separate_local_wrist_twist_x_vec, separate_local_wrist_twist_y_vec + cdef MVector3D separate_wrist_twist_x_vec, separate_wrist_twist_y_vec, test_local_wrist_twist_x_vec, test_local_wrist_twist_y_vec, test_local_wrist_origin_vec + cdef MVector3D test_wrist_twist_x_vec, test_wrist_twist_y_vec + cdef MQuaternion wrist_twist_result_qq, twist_x_qq, twist_y_qq, wrist_result_qq, wrist_twist_test_qq, twist_x_yz_qq, twist_y_yz_qq + cdef list wrist_qq_list, degree_list + cdef double wrist_twist_result_dot, wrist_twist_result_degree, twist_test_dot + cdef double twist_test_x_dot, twist_test_y_dot, wrist_result_dot, wrist_twist_test_degree + cdef double prev_wrist_result_dot, prev_wrist_twist_result_degree, prev_wrist_twist_result_dot - result_twist_qq = MQuaternion.fromAxisAndAngle(twist_x_axis, twist_degree) - # logger.test("fno: %s, total: %s, %s", fno, result_twist_qq.toDegree(), result_twist_qq) - - twisted_mat = MMatrix4x4() # 捩り分散後の回転量に基づく移動量 - twisted_mat.setToIdentity() # 初期化 - - # 手捩りの場合、腕まで見る - if grand_parent_x_axis: - twisted_mat.rotate(grand_parent_qq) # 腕の回転量 - twisted_mat.rotate(grand_parent_twist_qq) # 腕捩りの回転量 - twisted_mat.translate(grand_parent_x_axis) # 腕のX軸方向 - - twisted_mat.rotate(parent_qq) # 腕・ひじの回転量 - twisted_mat.rotate(result_twist_qq) # 腕捩り・手捩りの回転量 - twisted_mat.translate(parent_x_axis) # 腕・ひじのX軸方向 - twisted_mat.rotate(child_qq) # 元々のひじ・手首の回転量 - - # 手捩りの場合、Y軸でチェック - if grand_parent_x_axis: - twisted_vec = twisted_mat * child_y_axis # 手首のY軸方向 - else: - twisted_vec = twisted_mat * child_x_axis # ひじのX軸方向 + prev_wrist_result_qq = MQuaternion() + prev_wrist_result_dot = 0 + prev_wrist_twist_result_degree = 0 + prev_wrist_twist_result_dot = 0 + wrist_result_qq = wrist_qq + + # 手首 ------------ + + # オリジナル + original_wrist_mat = MMatrix4x4() + original_wrist_mat.setToIdentity() + original_wrist_mat.rotate(original_arm_qq) + original_wrist_mat.translate(arm_local_x_axis) + original_wrist_mat.rotate(original_arm_twist_qq) + original_wrist_mat.translate(arm_twist_local_x_axis) + original_wrist_mat.rotate(original_elbow_qq) + original_wrist_mat.translate(elbow_local_x_axis) + original_wrist_mat.rotate(original_wrist_twist_qq) + original_wrist_mat.translate(wrist_twist_local_x_axis) + original_wrist_mat.rotate(original_wrist_qq) + + # オリジナルの手首起点 + original_wrist_origin_vec = original_wrist_mat * MVector3D() + + # オリジナルの手首方向 + original_wrist_x_vec = original_wrist_mat * wrist_local_x_axis + original_wrist_y_vec = original_wrist_mat * wrist_local_y_axis + + # オリジナルから見たオリジナルの手首方向 + original_local_wrist_x_vec = original_wrist_mat.inverted() * original_wrist_x_vec + original_local_wrist_y_vec = original_wrist_mat.inverted() * original_wrist_y_vec + + i = 0 + # 手首 -------------- + while i < 5: + if i == 0: + # 初回は初期化 + wrist_twist_result_qq = MQuaternion.fromAxisAndAngle(wrist_twist_local_x_axis, wrist_twist_degree) + + # 分散後 + separate_wrist_mat = MMatrix4x4() + separate_wrist_mat.setToIdentity() + separate_wrist_mat.rotate(arm_qq) + separate_wrist_mat.translate(arm_local_x_axis) + separate_wrist_mat.rotate(arm_twist_qq) + separate_wrist_mat.translate(arm_twist_local_x_axis) + separate_wrist_mat.rotate(elbow_qq) + separate_wrist_mat.translate(elbow_local_x_axis) + separate_wrist_mat.rotate(wrist_twist_result_qq) + # separate_wrist_mat.rotate(wrist_result_qq) + + # オリジナルの手首原点に移動 + separate_local_wrist_origin_vec = separate_wrist_mat.inverted() * original_wrist_origin_vec + separate_wrist_mat.translate(separate_local_wrist_origin_vec) + + separate_wrist_mat.translate(wrist_twist_local_x_axis) + + # 分散後の手首方向 + separate_wrist_x_vec = separate_wrist_mat * wrist_local_x_axis + separate_wrist_y_vec = separate_wrist_mat * wrist_local_y_axis + + # オリジナルから見た分散後の手首方向 + separate_local_wrist_x_vec = original_wrist_mat.inverted() * separate_wrist_x_vec + separate_local_wrist_y_vec = original_wrist_mat.inverted() * separate_wrist_y_vec + + # 手首YZ + twist_x_qq = MQuaternion.rotationTo(separate_local_wrist_x_vec, original_local_wrist_x_vec) + twist_y_qq = MQuaternion.rotationTo(separate_local_wrist_y_vec, original_local_wrist_y_vec) + + # 分離 + _, _, _, twist_x_yz_qq = MServiceUtils.separate_local_qq(fno, wrist_bone_name, twist_x_qq, wrist_twist_local_x_axis) + _, _, _, twist_y_yz_qq = MServiceUtils.separate_local_qq(fno, wrist_bone_name, twist_y_qq, wrist_twist_local_x_axis) + + wrist_qq_list = [twist_x_yz_qq, twist_y_yz_qq] + + for j, wrist_result_qq in enumerate(wrist_qq_list): + # 初期化 + wrist_twist_result_dot = -999 + if i == 0: + # 初回だけ初期化 + wrist_twist_result_degree = wrist_twist_degree + wrist_twist_result_qq = MQuaternion.fromAxisAndAngle(wrist_twist_local_x_axis, wrist_twist_result_degree) + + # 手捩り ------------------ + + # 分散後 + separate_wrist_twist_mat = MMatrix4x4() + separate_wrist_twist_mat.setToIdentity() + separate_wrist_twist_mat.rotate(arm_qq) + separate_wrist_twist_mat.translate(arm_local_x_axis) + separate_wrist_twist_mat.rotate(arm_twist_qq) + separate_wrist_twist_mat.translate(arm_twist_local_x_axis) + separate_wrist_twist_mat.rotate(elbow_qq) + separate_wrist_twist_mat.translate(elbow_local_x_axis) + separate_wrist_twist_mat.rotate(wrist_twist_result_qq) + separate_wrist_twist_mat.translate(wrist_twist_local_x_axis) + + # オリジナルの手首原点に移動 + separate_local_wrist_origin_vec = separate_wrist_twist_mat.inverted() * original_wrist_origin_vec + separate_wrist_twist_mat.translate(separate_local_wrist_origin_vec) + + separate_wrist_twist_mat.rotate(wrist_result_qq) + + # 分散後の手首方向 + separate_wrist_twist_x_vec = separate_wrist_twist_mat * wrist_local_x_axis + separate_wrist_twist_y_vec = separate_wrist_twist_mat * wrist_local_y_axis + + # オリジナルから見た分散後の手首方向 + separate_local_wrist_twist_x_vec = original_wrist_mat.inverted() * separate_wrist_twist_x_vec + separate_local_wrist_twist_y_vec = original_wrist_mat.inverted() * separate_wrist_twist_y_vec + + # 手捩りの初期角度 + twist_x_qq = MQuaternion.rotationTo(separate_local_wrist_twist_x_vec, original_local_wrist_x_vec) + # twist_y_qq = MQuaternion.rotationTo(separate_local_wrist_twist_y_vec, original_local_wrist_y_vec) + + # オリジナルと分散後の差 + twist_test_x_dot = MVector3D.dotProduct(separate_local_wrist_twist_x_vec.normalized(), original_local_wrist_x_vec.normalized()) + twist_test_y_dot = MVector3D.dotProduct(separate_local_wrist_twist_y_vec.normalized(), original_local_wrist_y_vec.normalized()) + # wrist_result_dot = np.average([twist_test_x_dot, twist_test_y_dot], weights=[x_weight, 1 - x_weight]) + wrist_result_dot = np.mean([twist_test_x_dot, twist_test_y_dot]) + + degree_list = [twist_x_qq.toDegree(), -twist_x_qq.toDegree()] + logger.debug("候補(%s-%s) f: %s, %s, degree_list: %s, wrist: %s", i, j, fno, wrist_twist_bone_name, degree_list, wrist_result_qq.toDegree()) + + m = 0 + n = 0 + while m < 3 and n < 50: + for test_degree in degree_list: + wrist_twist_test_degree = wrist_twist_result_degree + test_degree + + # if abs(wrist_twist_test_degree) > 135: + # logger.debug("フリップ防止(%s-%s) f: %s, %s, n: %s(%s), wrist_twist_test_degree: %s", i, j, fno, wrist_twist_bone_name, n, m, wrist_twist_test_degree) + # wrist_twist_test_degree += (180 * np.sign(wrist_twist_test_degree) * -1) + + wrist_twist_test_qq = MQuaternion.fromAxisAndAngle(wrist_twist_local_x_axis, wrist_twist_test_degree) + + # 分散後 + test_wrist_twist_mat = MMatrix4x4() + test_wrist_twist_mat.setToIdentity() + test_wrist_twist_mat.rotate(arm_qq) + test_wrist_twist_mat.translate(arm_local_x_axis) + test_wrist_twist_mat.rotate(arm_twist_qq) + test_wrist_twist_mat.translate(arm_twist_local_x_axis) + test_wrist_twist_mat.rotate(elbow_qq) + test_wrist_twist_mat.translate(elbow_local_x_axis) + test_wrist_twist_mat.rotate(wrist_twist_test_qq) + test_wrist_twist_mat.translate(wrist_twist_local_x_axis) + + # オリジナルの手首原点に移動 + test_local_wrist_origin_vec = test_wrist_twist_mat.inverted() * original_wrist_origin_vec + test_wrist_twist_mat.translate(test_local_wrist_origin_vec) + + test_wrist_twist_mat.rotate(wrist_result_qq) + + # 分散後の手首方向 + test_wrist_twist_x_vec = test_wrist_twist_mat * wrist_local_x_axis + test_wrist_twist_y_vec = test_wrist_twist_mat * wrist_local_y_axis + + # オリジナルから見た分散後の手首方向 + test_local_wrist_twist_x_vec = original_wrist_mat.inverted() * test_wrist_twist_x_vec + test_local_wrist_twist_y_vec = original_wrist_mat.inverted() * test_wrist_twist_y_vec + + # オリジナルと分散後の差 + twist_test_x_dot = MVector3D.dotProduct(test_local_wrist_twist_x_vec.normalized(), original_local_wrist_x_vec.normalized()) + twist_test_y_dot = MVector3D.dotProduct(test_local_wrist_twist_y_vec.normalized(), original_local_wrist_y_vec.normalized()) + twist_test_dot = np.mean([twist_test_x_dot, twist_test_y_dot]) + + if twist_test_dot > wrist_twist_result_dot: + # より近くなった場合、角度採用 + logger.debug("〇(%s-%s) f: %s, %s, n: %s(%s), twist_test_dot: %s, twist_test_x_dot: %s, twist_test_y_dot: %s, test_degree: %s, wrist_twist_result_degree: %s, wrist_twist_test_degree: %s", \ + i, j, fno, wrist_twist_bone_name, n, m, twist_test_dot, twist_test_x_dot, twist_test_y_dot, test_degree, wrist_twist_result_degree, wrist_twist_test_degree) + + wrist_twist_result_dot = twist_test_dot + wrist_twist_result_degree = wrist_twist_test_degree + + twist_x_qq = MQuaternion.rotationTo(test_local_wrist_twist_x_vec, original_local_wrist_x_vec) + # twist_y_qq = MQuaternion.rotationTo(test_local_wrist_twist_y_vec, original_local_wrist_y_vec) + + if m > 0: + # 1回目以降の場合、角度を取り直して再チェック + m = 0 + degree_list = [twist_x_qq.toDegree(), -twist_x_qq.toDegree(), 2, -2] + break + else: + # 近付かなかったら不採用 + logger.debug("×(%s-%s) f: %s, %s, n: %s(%s), twist_test_dot: %s, twist_test_x_dot: %s, twist_test_y_dot: %s, test_degree: %s, wrist_twist_result_degree: %s, wrist_twist_test_degree: %s", \ + i, j, fno, wrist_twist_bone_name, n, m, twist_test_dot, twist_test_x_dot, twist_test_y_dot, test_degree, wrist_twist_result_degree, wrist_twist_test_degree) + + if wrist_twist_result_dot > RADIANS_1: + break + + if m == 0: + # 初回は最も近付いた回転量ベースでリスト再生成 + degree_list = [twist_x_qq.toDegree(), -twist_x_qq.toDegree(), 2, -2] + elif m > 0: + # 3回目以降でダメだった場合は、量を減らして再チェック + degree_list = [d * 0.5 for d in degree_list] + + m += 1 + n += 1 + + wrist_twist_result_qq = MQuaternion.fromAxisAndAngle(wrist_twist_local_x_axis, wrist_twist_result_degree) + logger.debug("仮設定(%s-%s) f: %s, %s, wrist_twist_result_dot: %s, wrist_twist_result_degree: %s", i, j, fno, wrist_twist_bone_name, wrist_twist_result_dot, wrist_twist_result_degree) + + logger.debug("(%s-%s) f: %s, %s, prev: %s(%s), now: %s(%s)", i, j, fno, wrist_twist_bone_name, prev_wrist_twist_result_degree, prev_wrist_twist_result_dot, wrist_twist_result_degree, wrist_twist_result_dot) + logger.debug("(%s-%s) f: %s, %s, prev: %s(%s), now: %s(%s)", i, j, fno, wrist_bone_name, prev_wrist_result_qq, prev_wrist_result_dot, wrist_result_qq, wrist_result_dot) - # 捩り分散後との差 - twisted_dot = MVector3D.dotProduct(original_vec.normalized(), twisted_vec.normalized()) + # 手首 --------------- - return twisted_dot, twist_degree, result_twist_qq + # 分散テスト + test_wrist_mat = MMatrix4x4() + test_wrist_mat.setToIdentity() + test_wrist_mat.rotate(arm_qq) + test_wrist_mat.translate(arm_local_x_axis) + test_wrist_mat.rotate(arm_twist_qq) + test_wrist_mat.translate(arm_twist_local_x_axis) + test_wrist_mat.rotate(elbow_qq) + test_wrist_mat.translate(elbow_local_x_axis) + test_wrist_mat.rotate(wrist_twist_result_qq) + test_wrist_mat.translate(wrist_twist_local_x_axis) + + # オリジナルの手首原点に移動 + test_local_wrist_origin_vec = test_wrist_mat.inverted() * original_wrist_origin_vec + test_wrist_mat.translate(test_local_wrist_origin_vec) + + test_wrist_mat.rotate(wrist_result_qq) + + # テストの手首方向 + test_wrist_x_vec = test_wrist_mat * wrist_local_x_axis + test_wrist_y_vec = test_wrist_mat * wrist_local_y_axis + + # オリジナルから見たテストの手首方向 + test_local_wrist_x_vec = original_wrist_mat.inverted() * test_wrist_x_vec + test_local_wrist_y_vec = original_wrist_mat.inverted() * test_wrist_y_vec + + # オリジナルと分散後の差 + twist_test_x_dot = MVector3D.dotProduct(test_local_wrist_x_vec.normalized(), original_local_wrist_x_vec.normalized()) + twist_test_y_dot = MVector3D.dotProduct(test_local_wrist_y_vec.normalized(), original_local_wrist_y_vec.normalized()) + wrist_result_dot = np.mean([twist_test_x_dot, twist_test_y_dot]) + + logger.debug("(%s-%s) f: %s, %s, wrist_result_dot: %s, twist_test_x_dot: %s, twist_test_y_dot: %s", i, j, fno, wrist_bone_name, wrist_result_dot, twist_test_x_dot, twist_test_y_dot) + + if wrist_result_dot < prev_wrist_result_dot and i > 0: + # 手首が近付いてない場合、この時点でNG + logger.debug("×手首却下 (%s-%s) f: %s, %s, wrist_result_dot: %s, prev_wrist_result_dot: %s", i, j, fno, wrist_bone_name, wrist_result_dot, prev_wrist_result_dot) + wrist_result_dot = prev_wrist_result_dot + wrist_result_qq = prev_wrist_result_qq + + logger.debug("仮置き(%s-%s) f: %s, %s, wrist_result_dot: %s, wrist_result_qq: %s", i, j, fno, wrist_bone_name, wrist_result_dot, wrist_result_qq) + + if RADIANS_2 < wrist_twist_result_dot and RADIANS_2 < wrist_result_dot and prev_wrist_twist_result_dot < wrist_twist_result_dot and prev_wrist_result_dot < wrist_result_dot: + # 充分に近い場合、終了 + break + + if prev_wrist_twist_result_dot > wrist_twist_result_dot: + # ダメだった場合、前回のを採用し直し + wrist_twist_result_dot = prev_wrist_twist_result_dot + wrist_twist_result_degree = prev_wrist_twist_result_degree + wrist_result_dot = prev_wrist_result_dot + wrist_result_qq = prev_wrist_result_qq + + # 前回分として次を実行する + prev_wrist_twist_result_dot = wrist_twist_result_dot + prev_wrist_twist_result_degree = wrist_twist_result_degree + prev_wrist_result_dot = wrist_result_dot + prev_wrist_result_qq = wrist_result_qq + + if RADIANS_5 < wrist_twist_result_dot and RADIANS_5 < wrist_result_dot: + # 充分に近い場合、終了 + break + + i += 1 + + # 確定 + wrist_twist_result_qq = MQuaternion.fromAxisAndAngle(wrist_twist_local_x_axis, wrist_twist_result_degree) + + if wrist_twist_result_dot < 0.9: + logger.warning("【No.%s】%sフレーム目:%s 捩り分散失敗: 角度: %s 近似度: %s", (data_set_idx + 1), fno, wrist_twist_bone_name, round(wrist_twist_result_degree, 3), round(wrist_twist_result_dot, 5)) + + logger.debug("f: %s, %s: %s, wrist_twist_result_qq: %s(%s)", fno, wrist_twist_bone_name, wrist_twist_result_dot, wrist_twist_result_degree, wrist_twist_result_qq) + logger.debug("f: %s, %s: %s, wrist_result_qq: %s(%s)", fno, wrist_bone_name, wrist_result_dot, wrist_result_qq.toDegree(), wrist_result_qq) + + return (wrist_twist_result_dot, wrist_twist_result_qq, wrist_result_dot, wrist_result_qq) # 足IK補正 - def adjust_leg_ik_stance(self, data_set_idx: int, data_set: MOptionsDataSet): + cdef bint adjust_leg_ik_stance(self, int data_set_idx, MOptionsDataSet data_set): logger.info("足IK補正 【No.%s】", (data_set_idx + 1), decoration=MLogger.DECORATION_LINE) futures = [] with ThreadPoolExecutor(thread_name_prefix="leg_ik{0}".format(data_set_idx), max_workers=min(2, self.options.max_workers)) as executor: for direction in ["左", "右"]: - futures.append(executor.submit(self.adjust_leg_ik_stance_lr, data_set_idx, direction)) + futures.append(executor.submit(self.adjust_leg_ik_stance_lr, self, data_set_idx, direction, 0)) concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) @@ -794,7 +1445,18 @@ def adjust_leg_ik_stance(self, data_set_idx: int, data_set: MOptionsDataSet): return True # 足IK補正 - def adjust_leg_ik_stance_lr(self, data_set_idx: int, direction: str): + cdef bint adjust_leg_ik_stance_lr(self, int data_set_idx, str direction, int dummy): + cdef MOptionsDataSet data_set + cdef str target_bone_name, org_ik_root_bone_name, rep_ik_root_bone_name, d_bone_name + cdef int prev_sep_fno, fno, fno_idx + cdef list fnos, ik_on_fnos, d_on_fnos + cdef BoneLinks org_ik_root_links, rep_ik_root_links, org_leg_ik_links, rep_leg_ik_links + cdef bint is_ik_on, is_d_on, is_in_ik_on + cdef VmdBoneFrame d_bf, ik_bf + cdef MMatrix4x4 org_leg_matrix, rep_leg_matrix, rep_leg_ik_matrix + cdef dict org_ik_root_global_3ds, org_leg_ik_global_3ds, rep_ik_root_global_3ds, rep_leg_ik_global_3ds, rep_initial_leg_ik_matrixs + cdef MVector3D org_global_leg_ik_pos, org_local_leg_ik_pos, rep_global_leg_ik_pos, rep_local_leg_ik_pos, recalc_rep_global_leg_ik_pos, rep_leg_ik_recalc_local_pos, leg_ratio + try: logger.copy(self.options) data_set = self.options.data_set_list[data_set_idx] @@ -840,7 +1502,7 @@ def adjust_leg_ik_stance_lr(self, data_set_idx: int, direction: str): if len(fnos) == 0: logger.info("%s足IK補正: 【No.%s】処理対象キーフレがないため、処理を終了します。", direction, (data_set_idx + 1)) - return + return True ik_on_fnos = [] d_on_fnos = [] @@ -979,13 +1641,13 @@ def adjust_leg_ik_stance_lr(self, data_set_idx: int, direction: str): raise e # つま先IK補正 - def adjust_toe_ik_stance(self, data_set_idx: int, data_set: MOptionsDataSet): + cdef bint adjust_toe_ik_stance(self, int data_set_idx, MOptionsDataSet data_set): logger.info("つま先IK補正 【No.%s】", (data_set_idx + 1), decoration=MLogger.DECORATION_LINE) futures = [] with ThreadPoolExecutor(thread_name_prefix="toe_ik{0}".format(data_set_idx), max_workers=min(2, self.options.max_workers)) as executor: for direction in ["左", "右"]: - futures.append(executor.submit(self.adjust_toe_ik_stance_lr, data_set_idx, direction)) + futures.append(executor.submit(self.adjust_toe_ik_stance_lr, self, data_set_idx, direction, 0.0)) concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) @@ -1003,7 +1665,18 @@ def adjust_toe_ik_stance(self, data_set_idx: int, data_set: MOptionsDataSet): return True # つま先IK補正 - def adjust_toe_ik_stance_lr(self, data_set_idx: int, direction: str): + cdef bint adjust_toe_ik_stance_lr(self, int data_set_idx, str direction, double dummy): + cdef MOptionsDataSet data_set + cdef str toe_ik_bone_name , leg_ik_bone_name , ankle_bone_name , leg_bone_name , leg_ik_parent_name + cdef list toe_ik_target_bones , fnos , ik_on_fnos , d_on_fnos , org_initial_toe_trans_vs + cdef int fno_idx, fno, prev_sep_fno + cdef BoneLinks org_toe_ik_links , org_leg_links + cdef bint is_ik_on , is_d_on , is_in_ik_on + cdef VmdBoneFrame d_bf , leg_ik_bf , toe_ik_bf + cdef MQuaternion org_leg_direction_qq , toe_qq + cdef MVector3D org_target_global_toe_ik_pos , org_initial_global_toe_ik_pos , initial_local_toe_ik_pos , target_local_toe_ik_pos + cdef MMatrix4x4 org_initial_leg_ik_matrix + try: logger.copy(self.options) data_set = self.options.data_set_list[data_set_idx] @@ -1018,11 +1691,17 @@ def adjust_toe_ik_stance_lr(self, data_set_idx: int, direction: str): if set(toe_ik_target_bones).issubset(data_set.org_model.bones) and set(toe_ik_target_bones).issubset(data_set.rep_model.bones): prev_sep_fno = 0 + is_execed_toe_ik = False if toe_ik_bone_name in data_set.org_model.bones and toe_ik_bone_name in data_set.rep_model.bones \ and toe_ik_bone_name in data_set.motion.bones and data_set.motion.is_active_bones(toe_ik_bone_name): # ボーンとモーションが揃ってある場合のみ補正 + if len(data_set.motion.get_bone_fnos(toe_ik_bone_name)) <= 1: + # 0Fキーはあっても無視 + logger.info("%sつま先IK補正: 【No.%s】処理対象キーフレがないため、処理を終了します。", direction, (data_set_idx + 1)) + return True + logger.info("%s補正【No.%s】", toe_ik_bone_name, (data_set_idx + 1)) org_toe_ik_links = data_set.org_model.create_link_2_top_one(toe_ik_bone_name) @@ -1034,9 +1713,9 @@ def adjust_toe_ik_stance_lr(self, data_set_idx: int, direction: str): fnos.extend(data_set.motion.get_differ_fnos((data_set_idx + 1), [leg_ik_bone_name, toe_ik_bone_name], limit_degrees=20, limit_length=1.5)) fnos = sorted(list(set(fnos))) - if len(fnos) == 0: + if len(fnos) <= 1: logger.info("%sつま先IK補正: 【No.%s】処理対象キーフレがないため、処理を終了します。", direction, (data_set_idx + 1)) - return + return True ik_on_fnos = [] d_on_fnos = [] @@ -1097,6 +1776,8 @@ def adjust_toe_ik_stance_lr(self, data_set_idx: int, direction: str): if round(toe_ik_bf.position.x(), 2) == 0 and round(toe_ik_bf.position.y(), 2) == 0 and round(toe_ik_bf.position.z(), 2) == 0: # つま先IKにキーがあっても値がなければスルー continue + + is_execed_toe_ik = True # 元モデルデータ -------- @@ -1156,8 +1837,9 @@ def adjust_toe_ik_stance_lr(self, data_set_idx: int, direction: str): logger.info("-- %sフレーム目:終了(%s%)【No.%s - %s補正】", fno, round((fno / fnos[-1]) * 100, 3), data_set_idx + 1, toe_ik_bone_name) prev_sep_fno = fno // 500 - # self.remove_unnecessary_bf_pool_parts(data_set_idx, leg_ik_bone_name, 0) - # self.remove_unnecessary_bf_pool_parts(data_set_idx, toe_ik_bone_name, 0) + # if is_execed_toe_ik: + # self.remove_unnecessary_bf_pool_parts(data_set_idx, leg_ik_bone_name, 0) + # self.remove_unnecessary_bf_pool_parts(data_set_idx, toe_ik_bone_name, 0) logger.info("%sつま先IK補正:終了【No.%s】", direction, (data_set_idx + 1)) else: @@ -1175,13 +1857,13 @@ def adjust_toe_ik_stance_lr(self, data_set_idx: int, direction: str): raise e # つま先補正 - def adjust_toe_stance(self, data_set_idx: int, data_set: MOptionsDataSet): + cdef bint adjust_toe_stance(self, int data_set_idx, MOptionsDataSet data_set): logger.info("つま先補正 【No.%s】", (data_set_idx + 1), decoration=MLogger.DECORATION_LINE) futures = [] with ThreadPoolExecutor(thread_name_prefix="toe{0}".format(data_set_idx), max_workers=min(2, self.options.max_workers)) as executor: for direction in ["左", "右"]: - futures.append(executor.submit(self.adjust_toe_stance_lr, data_set_idx, direction)) + futures.append(executor.submit(self.adjust_toe_stance_lr, self, data_set_idx, direction, "")) concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) @@ -1199,7 +1881,16 @@ def adjust_toe_stance(self, data_set_idx: int, data_set: MOptionsDataSet): return True # つま先補正 - def adjust_toe_stance_lr(self, data_set_idx: int, direction: str): + cdef bint adjust_toe_stance_lr(self, int data_set_idx, str direction, str dummy): + cdef double adjust_sole_y, adjust_toe_y, org_sole_diff, org_toe_diff, org_toe_limit, rep_sole_diff, rep_toe_diff, rep_toe_limit, sole_diff, toe_diff, toe_limit_ratio + cdef MOptionsDataSet data_set + cdef list fnos, toe_target_bones + cdef VmdBoneFrame ik_bf + cdef str ik_bone_name + cdef BoneLinks org_toe_links, rep_toe_links + cdef MVector3D org_toe_pos, org_sole_pos, rep_toe_pos, rep_sole_pos + cdef int fno_idx, fno, prev_sep_fno + try: logger.copy(self.options) data_set = self.options.data_set_list[data_set_idx] @@ -1242,16 +1933,16 @@ def adjust_toe_stance_lr(self, data_set_idx: int, direction: str): rep_toe_pos, rep_sole_pos = self.get_toe_entity(data_set_idx, data_set, data_set.rep_model, data_set.motion, rep_toe_links, ik_bone_name, fno) # つま先が元モデルの上にある場合、つま先を合わせて下に下ろす(実体を考慮する) - toe_diff = ((org_toe_pos.y() - data_set.org_model.bones["{0}つま先実体".format(ik_bone_name[0])].position.y()) * toe_limit_ratio) \ - - (rep_toe_pos.y() - data_set.rep_model.bones["{0}つま先実体".format(ik_bone_name[0])].position.y()) \ - + (data_set.rep_model.bones["{0}つま先実体".format(ik_bone_name[0])].position.y() - (data_set.org_model.bones["{0}つま先実体".format(ik_bone_name[0])].position.y() * toe_limit_ratio)) - logger.test("f: %s, %s - toe_diff: %s", fno, ik_bone_name[0], toe_diff) + toe_diff = ((org_toe_pos.y() - data_set.org_model.bones["{0}つま先実体".format(ik_bone_name[int(0)])].position.y()) * toe_limit_ratio) \ + - (rep_toe_pos.y() - data_set.rep_model.bones["{0}つま先実体".format(ik_bone_name[int(0)])].position.y()) \ + + (data_set.rep_model.bones["{0}つま先実体".format(ik_bone_name[int(0)])].position.y() - (data_set.org_model.bones["{0}つま先実体".format(ik_bone_name[int(0)])].position.y() * toe_limit_ratio)) + logger.test("f: %s, %s - toe_diff: %s", fno, ik_bone_name[int(0)], toe_diff) # 足底が元モデルの上にある場合、足底を合わせて下に下ろす(実体を考慮する) - sole_diff = (rep_sole_pos.y() - data_set.rep_model.bones["{0}足底実体".format(ik_bone_name[0])].position.y()) \ - - ((org_sole_pos.y() - data_set.org_model.bones["{0}足底実体".format(ik_bone_name[0])].position.y()) * toe_limit_ratio) \ - + (data_set.rep_model.bones["{0}足底実体".format(ik_bone_name[0])].position.y() - (data_set.org_model.bones["{0}足底実体".format(ik_bone_name[0])].position.y() * toe_limit_ratio)) - logger.test("f: %s, %s - sole_diff: %s", fno, ik_bone_name[0], sole_diff) + sole_diff = (rep_sole_pos.y() - data_set.rep_model.bones["{0}足底実体".format(ik_bone_name[int(0)])].position.y()) \ + - ((org_sole_pos.y() - data_set.org_model.bones["{0}足底実体".format(ik_bone_name[int(0)])].position.y()) * toe_limit_ratio) \ + + (data_set.rep_model.bones["{0}足底実体".format(ik_bone_name[int(0)])].position.y() - (data_set.org_model.bones["{0}足底実体".format(ik_bone_name[int(0)])].position.y() * toe_limit_ratio)) + logger.test("f: %s, %s - sole_diff: %s", fno, ik_bone_name[int(0)], sole_diff) if rep_toe_pos.y() <= rep_sole_pos.y() and org_toe_pos.y() < org_toe_limit: # 足底よりつま先のが下の場合(つま先立ち) @@ -1266,8 +1957,8 @@ def adjust_toe_stance_lr(self, data_set_idx: int, direction: str): data_set.motion.regist_bf(ik_bf, "{0}足IK".format(direction), fno) elif rep_sole_pos.y() < rep_toe_pos.y() and org_sole_pos.y() < org_toe_limit: # 足先のがつま先より下の場合(接地) - org_sole_diff = (org_sole_pos.y() - data_set.org_model.bones["{0}足底実体".format(ik_bone_name[0])].position.y()) * toe_limit_ratio - rep_sole_diff = rep_sole_pos.y() - data_set.rep_model.bones["{0}足底実体".format(ik_bone_name[0])].position.y() + org_sole_diff = (org_sole_pos.y() - data_set.org_model.bones["{0}足底実体".format(ik_bone_name[int(0)])].position.y()) * toe_limit_ratio + rep_sole_diff = rep_sole_pos.y() - data_set.rep_model.bones["{0}足底実体".format(ik_bone_name[int(0)])].position.y() # 足IKを動かして、足底の位置を合わせる adjust_sole_y = ik_bf.position.y() + (org_sole_diff - rep_sole_diff) @@ -1299,19 +1990,30 @@ def adjust_toe_stance_lr(self, data_set_idx: int, direction: str): raise e # つま先実体のグローバル位置を取得する - def get_toe_entity(self, data_set_idx: int, data_set: MOptionsDataSet, model: PmxModel, motion: VmdMotion, toe_links: BoneLinks, ik_bone_name: str, fno: int): + cdef tuple get_toe_entity(self, int data_set_idx, MOptionsDataSet data_set, PmxModel model, VmdMotion motion, BoneLinks toe_links, str ik_bone_name, int fno): + cdef dict toe_3ds + cdef MVector3D toe_pos, sole_pos + toe_3ds = MServiceUtils.calc_global_pos(model, toe_links, motion, fno) logger.test(model.name) [logger.test("-- %s: %s", k, v) for k, v in toe_3ds.items()] - toe_pos = toe_3ds["{0}つま先実体".format(ik_bone_name[0])] - sole_pos = toe_3ds["{0}足底実体".format(ik_bone_name[0])] + toe_pos = toe_3ds["{0}つま先実体".format(ik_bone_name[int(0)])] + sole_pos = toe_3ds["{0}足底実体".format(ik_bone_name[int(0)])] return toe_pos, sole_pos # センターXZ補正 - def adjust_center_stance(self, data_set_idx: int, data_set: MOptionsDataSet): + cdef bint adjust_center_stance(self, int data_set_idx, MOptionsDataSet data_set): + cdef BoneLinks org_center_links, org_upper_links, org_lower_links + cdef dict org_leg_ik_links, org_leg_links + cdef BoneLinks rep_center_links, rep_upper_links, rep_lower_links + cdef dict rep_leg_ik_links, rep_leg_links + cdef list fnos + cdef int fno, prev_sep_fno + cdef VmdBoneFrame bf + logger.info("センターXZ補正 【No.%s】", (data_set_idx + 1), decoration=MLogger.DECORATION_LINE) # センター調整に必要なボーン群 @@ -1371,11 +2073,22 @@ def adjust_center_stance(self, data_set_idx: int, data_set: MOptionsDataSet): proccess_key = "【No.{0}】{1}({2})".format(data_set_idx + 1, os.path.basename(data_set.motion.path), data_set.rep_model.name) self.options.tree_process_dict[proccess_key]["スタンス追加補正"]["センターXZ補正"] = True - + return True # センターY補正 - def adjust_center_arm_stance(self, data_set_idx: int, data_set: MOptionsDataSet): + cdef bint adjust_center_arm_stance(self, int data_set_idx, MOptionsDataSet data_set): + cdef str org_center_bone_name, rep_center_bone_name + cdef BoneLinks org_center_links + cdef dict org_arm_links, org_leg_links + cdef BoneLinks rep_center_links + cdef dict rep_arm_links, rep_leg_links + cdef double org_right_palm_length, org_left_palm_length, org_palm_length, rep_right_palm_length, rep_left_palm_length, rep_palm_length + cdef double org_upper_length, rep_upper_length + cdef int fno, prev_sep_fno + cdef list fnos + cdef VmdBoneFrame center_bf, groove_bf, bf + logger.info("センターY補正 【No.%s】", (data_set_idx + 1), decoration=MLogger.DECORATION_LINE) # センター調整に必要なボーン群(腕チェック済み) @@ -1456,15 +2169,19 @@ def adjust_center_arm_stance(self, data_set_idx: int, data_set: MOptionsDataSet) proccess_key = "【No.{0}】{1}({2})".format(data_set_idx + 1, os.path.basename(data_set.motion.path), data_set.rep_model.name) self.options.tree_process_dict[proccess_key]["スタンス追加補正"]["センターY補正"] = True - + return True # 足IKによるセンターオフセット値 - def calc_center_offset_by_arm(self, bf: VmdBoneFrame, data_set_idx: int, data_set: MOptionsDataSet, \ - org_center_links: BoneLinks, org_arm_links: BoneLinks, org_leg_links: BoneLinks, \ - rep_center_links: BoneLinks, rep_arm_links: BoneLinks, rep_leg_links: BoneLinks, \ - org_palm_length: float, rep_palm_length: float, \ - org_center_bone_name: str, rep_center_bone_name: str, org_upper_length: float, rep_upper_length: float): + cdef MVector3D calc_center_offset_by_arm(self, VmdBoneFrame bf, int data_set_idx, MOptionsDataSet data_set, \ + BoneLinks org_center_links, dict org_arm_links, dict org_leg_links, \ + BoneLinks rep_center_links, dict rep_arm_links, dict rep_leg_links, \ + double org_palm_length, double rep_palm_length, \ + str org_center_bone_name, str rep_center_bone_name, double org_upper_length, double rep_upper_length): + + cdef MVector3D org_left_wrist_pos, org_right_wrist_pos, org_neck_base_pos, org_left_leg_pos, org_right_leg_pos, org_lower_pos + cdef MVector3D rep_left_wrist_pos, rep_right_wrist_pos, rep_neck_base_pos, rep_left_leg_pos, rep_right_leg_pos, rep_lower_pos, rep_center_arm_offset + cdef double org_left_wrist_offset, org_right_wrist_offset, org_left_leg_offset, org_right_leg_offset # 元モデルのセンターオフセット org_left_wrist_pos, org_right_wrist_pos, org_neck_base_pos, org_left_leg_pos, org_right_leg_pos, org_lower_pos = \ @@ -1520,18 +2237,20 @@ def calc_center_offset_by_arm(self, bf: VmdBoneFrame, data_set_idx: int, data_se rep_center_arm_offset = MVector3D() target_offsets = np.array([org_left_wrist_offset, org_right_wrist_offset, org_left_leg_offset, org_right_leg_offset]) - if len(np.nonzero(target_offsets)[0]) > 0: + if len(np.nonzero(target_offsets)[int(0)]) > 0: # 床に潜るよりは浮いてる方がマシ - rep_center_arm_offset = MVector3D(0, target_offsets[np.argmin(np.abs(target_offsets[np.nonzero(target_offsets)[0]]))], 0) + rep_center_arm_offset = MVector3D(0, target_offsets[np.argmin(np.abs(target_offsets[np.nonzero(target_offsets)[int(0)]]))], 0) logger.debug("センターY補正(結果) f: %s, y: %s", bf.fno, rep_center_arm_offset.y()) return rep_center_arm_offset # モデル別足IKによるセンターオフセット値 - def calc_center_offset_by_arm_model(self, bf: VmdBoneFrame, data_set_idx: int, data_set: MOptionsDataSet, \ - model: PmxModel, motion: VmdMotion, \ - center_links: BoneLinks, arm_links: BoneLinks, leg_links: BoneLinks, center_bone_name: str): + cdef tuple calc_center_offset_by_arm_model(self, VmdBoneFrame bf, int data_set_idx, MOptionsDataSet data_set, \ + PmxModel model, VmdMotion motion, \ + BoneLinks center_links, dict arm_links, dict leg_links, str center_bone_name): + + cdef dict left_arm_global_3ds, right_arm_global_3ds, left_leg_global_3ds, right_leg_global_3ds # 左手首までの位置 left_arm_global_3ds = MServiceUtils.calc_global_pos(model, arm_links["左"], motion, bf.fno) @@ -1549,10 +2268,14 @@ def calc_center_offset_by_arm_model(self, bf: VmdBoneFrame, data_set_idx: int, d left_leg_global_3ds["左足"], right_leg_global_3ds["右足"], right_leg_global_3ds["下半身"] # 足IKによるセンターオフセット値 - def calc_center_offset_by_leg_ik(self, bf: VmdBoneFrame, data_set_idx: int, data_set: MOptionsDataSet, \ - org_center_links: BoneLinks, org_leg_ik_links: BoneLinks, \ - rep_center_links: BoneLinks, rep_leg_ik_links: BoneLinks, \ - org_center_bone_name: str, rep_center_bone_name: str): + cdef MVector3D calc_center_offset_by_leg_ik(self, VmdBoneFrame bf, int data_set_idx, MOptionsDataSet data_set, \ + BoneLinks org_center_links, dict org_leg_ik_links, \ + BoneLinks rep_center_links, dict rep_leg_ik_links, \ + str org_center_bone_name, str rep_center_bone_name): + + cdef MVector3D front_center_ik_offset, org_front_center_ik_offset, rep_front_center_ik_offset + cdef MQuaternion org_center_direction_qq, rep_center_direction_qq + cdef dict rotated_center_3ds # 元モデルのセンターオフセット org_front_center_ik_offset, org_center_direction_qq = \ @@ -1576,9 +2299,13 @@ def calc_center_offset_by_leg_ik(self, bf: VmdBoneFrame, data_set_idx: int, data return rotated_center_3ds[rep_center_bone_name] # モデル別足IKによるセンターオフセット値 - def calc_center_offset_by_leg_ik_model(self, bf: VmdBoneFrame, data_set_idx: int, data_set: MOptionsDataSet, \ - model: PmxModel, motion: VmdMotion, \ - center_links: BoneLinks, leg_ik_links: BoneLinks, center_bone_name: str): + cdef tuple calc_center_offset_by_leg_ik_model(self, VmdBoneFrame bf, int data_set_idx, MOptionsDataSet data_set, \ + PmxModel model, VmdMotion motion, \ + BoneLinks center_links, dict leg_ik_links, str center_bone_name): + + cdef MQuaternion center_direction_qq, left_leg_ik_direction_qq, right_leg_ik_direction_qq + cdef dict center_global_3ds, front_center_global_3ds, front_left_leg_ik_global_3ds, front_right_leg_ik_global_3ds, left_leg_ik_global_3ds, right_leg_ik_global_3ds + cdef MVector3D front_center_ik_offset, front_center_pos, front_left_ik_pos, front_right_ik_pos # センターまでの位置 center_global_3ds, front_center_global_3ds, center_direction_qq = \ @@ -1604,10 +2331,14 @@ def calc_center_offset_by_leg_ik_model(self, bf: VmdBoneFrame, data_set_idx: int return front_center_ik_offset, center_direction_qq # 体幹によるセンターオフセット値 - def calc_center_offset_by_trunk(self, bf: VmdBoneFrame, data_set_idx: int, data_set: MOptionsDataSet, \ - org_center_links: BoneLinks, org_upper_links: BoneLinks, org_lower_links: BoneLinks, org_leg_links: BoneLinks, \ - rep_center_links: BoneLinks, rep_upper_links: BoneLinks, rep_lower_links: BoneLinks, rep_leg_links: BoneLinks, \ - org_center_bone_name: str, rep_center_bone_name: str): + cdef MVector3D calc_center_offset_by_trunk(self, VmdBoneFrame bf, int data_set_idx, MOptionsDataSet data_set, \ + BoneLinks org_center_links, BoneLinks org_upper_links, BoneLinks org_lower_links, dict org_leg_links, \ + BoneLinks rep_center_links, BoneLinks rep_upper_links, BoneLinks rep_lower_links, dict rep_leg_links, \ + str org_center_bone_name, str rep_center_bone_name): + cdef MVector3D center_trunk_diff, front_lower_center_diff, front_upper_center_diff, org_front_lower_center_diff, org_front_upper_center_diff, + cdef MVector3D rep_front_lower_center_diff, rep_front_upper_center_diff + cdef MQuaternion org_lower_direction_qq, org_upper_direction_qq, rep_lower_direction_qq, rep_upper_direction_qq + cdef dict rotated_lower_center_3ds, rotated_upper_center_3ds # 元モデルのセンター差分 org_front_upper_center_diff, org_front_lower_center_diff, org_upper_direction_qq, org_lower_direction_qq = \ @@ -1639,10 +2370,15 @@ def calc_center_offset_by_trunk(self, bf: VmdBoneFrame, data_set_idx: int, data_ return center_trunk_diff - def calc_center_offset_by_trunk_model(self, bf: VmdBoneFrame, data_set_idx: int, data_set: MOptionsDataSet, \ - model: PmxModel, motion: VmdMotion, \ - center_links: BoneLinks, upper_links: BoneLinks, lower_links: BoneLinks, \ - center_bone_name: str): + cdef tuple calc_center_offset_by_trunk_model(self, VmdBoneFrame bf, int data_set_idx, MOptionsDataSet data_set, \ + PmxModel model, VmdMotion motion, \ + BoneLinks center_links, BoneLinks upper_links, BoneLinks lower_links, \ + str center_bone_name): + cdef VmdBoneFrame calc_bf + cdef MVector3D front_lower_center_diff, front_upper_center_diff + cdef MQuaternion center_direction_qq, lower_direction_qq, upper_direction_qq + cdef dict front_center_global_3ds, front_lower_global_3ds, front_upper_global_3ds, center_global_3ds, lower_global_3ds, upper_global_3ds + cdef VmdMotion lower_motion, upper_motion # センターまでの位置 center_global_3ds, front_center_global_3ds, center_direction_qq = \ @@ -1691,7 +2427,19 @@ def calc_center_offset_by_trunk_model(self, bf: VmdBoneFrame, data_set_idx: int, return front_upper_center_diff, front_lower_center_diff, upper_direction_qq, lower_direction_qq # 上半身補正 - def adjust_upper_stance(self, data_set_idx: int, data_set: MOptionsDataSet): + cdef bint adjust_upper_stance(self, int data_set_idx, MOptionsDataSet data_set): + cdef bint is_upper2_existed + cdef int prev_fno, fno + cdef double dot, dot2_limit, dot_limit + cdef list fnos, upper2_target_bones, upper_target_bones + cdef VmdBoneFrame initial_bf, upper2_bf, upper_bf + cdef MOptionsDataSet initial_dataset + cdef dict org_arm_links, rep_arm_links + cdef BoneLinks org_head_links, org_upper2_links, org_upper_links, rep_head_links, rep_upper2_links, rep_upper_links + cdef MVector3D org_upper2_slope, org_upper_slope, rep_upper2_slope, rep_upper2_slope_cross, rep_upper2_slope_up, rep_upper_slope, rep_upper_slope_cross, rep_upper_slope_up + cdef MVector3D arm_diff_ratio, org_arm_diff, org_to_diff, rep_arm_diff, ratio, rep_to_diff, to_diff_ratio + cdef MQuaternion rep_upper2_initial_slope_qq, rep_upper_initial_slope_qq, upper2_initial_qq, upper_initial_qq + logger.info("上半身補正 【No.%s】", (data_set_idx + 1), decoration=MLogger.DECORATION_LINE) # 上半身調整に必要なボーン群 @@ -1737,6 +2485,7 @@ def adjust_upper_stance(self, data_set_idx: int, data_set: MOptionsDataSet): # 肩幅比率 org_arm_diff = (org_arm_links["左"].get("左腕").position - org_arm_links["右"].get("右腕").position) + org_arm_diff.one() rep_arm_diff = (rep_arm_links["左"].get("左腕").position - rep_arm_links["右"].get("右腕").position) arm_diff_ratio = rep_arm_diff / org_arm_diff arm_diff_ratio.one() # 比率なので、0は1に変換する @@ -1745,7 +2494,6 @@ def adjust_upper_stance(self, data_set_idx: int, data_set: MOptionsDataSet): org_to_diff = (org_head_links.get("頭").position - org_head_links.get("上半身").position) org_to_diff.one() rep_to_diff = (rep_head_links.get("頭").position - rep_head_links.get("上半身").position) - rep_to_diff.one() to_diff_ratio = rep_to_diff / org_to_diff logger.test("arm_diff_ratio: %s", arm_diff_ratio) @@ -1838,7 +2586,6 @@ def adjust_upper_stance(self, data_set_idx: int, data_set: MOptionsDataSet): org_to_diff = (org_head_links.get("頭").position - org_head_links.get("上半身2").position) org_to_diff.one() rep_to_diff = (rep_head_links.get("頭").position - rep_head_links.get("上半身2").position) - rep_to_diff.one() to_diff_ratio = rep_to_diff / org_to_diff logger.test("arm_diff_ratio: %s", arm_diff_ratio) @@ -1914,7 +2661,18 @@ def adjust_upper_stance(self, data_set_idx: int, data_set: MOptionsDataSet): return True # 下半身補正 - def adjust_lower_stance(self, data_set_idx: int, data_set: MOptionsDataSet): + cdef bint adjust_lower_stance(self, int data_set_idx, MOptionsDataSet data_set): + cdef int fno, prev_fno + cdef double dot, dot_limit + cdef list fnos, lower_target_bones + cdef VmdBoneFrame initial_bf, lower_bf + cdef MOptionsDataSet initial_dataset + cdef MQuaternion lower_initial_qq, rep_lower_initial_slope_qq + cdef dict org_leg_links, rep_leg_links + cdef BoneLinks org_leg_center_links, org_lower_links, rep_leg_center_links, rep_lower_links + cdef MVector3D org_lower_slope, rep_lower_slope, rep_lower_slope_cross, rep_lower_slope_up + cdef MVector3D leg_diff_ratio, org_leg_diff, org_to_diff, ratio, rep_leg_diff, rep_to_diff, to_diff_ratio + logger.info("下半身補正 【No.%s】", (data_set_idx + 1), decoration=MLogger.DECORATION_LINE) # 下半身調整に必要なボーン群 @@ -1953,15 +2711,15 @@ def adjust_lower_stance(self, data_set_idx: int, data_set: MOptionsDataSet): # 肩幅比率 org_leg_diff = (org_leg_links["左"].get("左足").position - org_leg_links["右"].get("右足").position) + org_leg_diff.one() rep_leg_diff = (rep_leg_links["左"].get("左足").position - rep_leg_links["右"].get("右足").position) leg_diff_ratio = rep_leg_diff / org_leg_diff leg_diff_ratio.non_zero() # 比率なので、0は1に変換する # TOの長さ比率 org_to_diff = (org_leg_center_links.get("足中間").position - org_leg_center_links.get("下半身").position) - org_to_diff.non_zero() + org_to_diff.one() rep_to_diff = (rep_leg_center_links.get("足中間").position - rep_leg_center_links.get("下半身").position) - rep_to_diff.non_zero() to_diff_ratio = rep_to_diff / org_to_diff logger.test("leg_diff_ratio: %s", leg_diff_ratio) @@ -2036,11 +2794,19 @@ def adjust_lower_stance(self, data_set_idx: int, data_set: MOptionsDataSet): return True # 体幹スタンス補正 - def calc_rotation_stance_trunk(self, bf: VmdBoneFrame, data_set_idx: int, data_set: MOptionsDataSet, \ - org_from_links: BoneLinks, org_to_links: BoneLinks, org_arm_links: BoneLinks, \ - rep_from_links: BoneLinks, rep_to_links: BoneLinks, rep_arm_links: BoneLinks, \ - from_bone_name: str, to_bone_name: str, rep_parent_bone_name: str, ratio: MVector3D, \ - rep_initial_slope_qq: MQuaternion, cancel_qq: MQuaternion, dot_limit: float, up_name: str): + cdef bint calc_rotation_stance_trunk(self, VmdBoneFrame bf, int data_set_idx, MOptionsDataSet data_set, \ + BoneLinks org_from_links, BoneLinks org_to_links, dict org_arm_links, \ + BoneLinks rep_from_links, BoneLinks rep_to_links, dict rep_arm_links, \ + str from_bone_name, str to_bone_name, str rep_parent_bone_name, MVector3D ratio, \ + MQuaternion rep_initial_slope_qq, MQuaternion cancel_qq, double dot_limit, str up_name): + cdef double rep_front_to_x, rep_front_to_y, rep_front_to_z, uad + cdef dict new_rep_front_to_global_3ds, org_from_global_3ds, org_front_from_global_3ds, org_front_to_global_3ds, org_left_arm_global_3ds, + cdef dict org_right_arm_global_3ds, org_to_global_3ds, rep_from_global_3ds, rep_front_from_global_3ds, rep_front_to_global_3ds, rep_to_global_3ds, rotated_to_3ds + cdef MVector3D direction, new_rep_front_to_pos, new_rep_to_pos, org_front_from_pos, org_front_to_pos, org_left_arm_pos, org_right_arm_pos + cdef MVector3D rep_from_pos, rep_front_from_pos, rep_front_to_pos, rep_to_pos, up, up_pos + cdef MQuaternion from_orientation, from_rotation, initial, org_from_direction_qq, org_to_direction_qq, parent_qq, rep_from_direction_qq, rep_to_direction_qq + cdef VmdBoneFrame org_bf + logger.test("f: %s -----------------------------", bf.fno) # 基準より親の回転量 @@ -2148,15 +2914,17 @@ def calc_rotation_stance_trunk(self, bf: VmdBoneFrame, data_set_idx: int, data_s else: # 元にもない場合(ないはず)、はそのまま設定 bf.rotation = from_rotation + + return True # 肩補正 - def adjust_shoulder_stance(self, data_set_idx: int, data_set: MOptionsDataSet): + cdef bint adjust_shoulder_stance(self, int data_set_idx, MOptionsDataSet data_set): logger.info("肩補正 【No.%s】", (data_set_idx + 1), decoration=MLogger.DECORATION_LINE) futures = [] with ThreadPoolExecutor(thread_name_prefix="shoulder{0}".format(data_set_idx), max_workers=min(2, self.options.max_workers)) as executor: for direction in ["左", "右"]: - futures.append(executor.submit(self.adjust_shoulder_stance_lr, data_set_idx, "{0}肩P".format(direction), "{0}肩".format(direction), "{0}腕".format(direction))) + futures.append(executor.submit(self.adjust_shoulder_stance_lr, self, data_set_idx, "{0}肩P".format(direction), "{0}肩".format(direction), "{0}腕".format(direction))) concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) @@ -2174,7 +2942,13 @@ def adjust_shoulder_stance(self, data_set_idx: int, data_set: MOptionsDataSet): return True # 肩補正左右 - def adjust_shoulder_stance_lr(self, data_set_idx: int, shoulder_p_name: str, shoulder_name: str, arm_name: str): + cdef bint adjust_shoulder_stance_lr(self, int data_set_idx, str shoulder_p_name, str shoulder_name, str arm_name): + cdef MOptionsDataSet data_set + cdef double dot + cdef list shoulder_target_bones + cdef bint is_shoulder_p + cdef MVector3D org_shoulder_diff, org_shoulder_slope, rep_shoulder_diff, rep_shoulder_slope, shoulder_diff_ratio + try: logger.copy(self.options) data_set = self.options.data_set_list[data_set_idx] @@ -2197,6 +2971,7 @@ def adjust_shoulder_stance_lr(self, data_set_idx: int, shoulder_p_name: str, sho logger.info("【No.%s】%s - 向きの近似度: %s", (data_set_idx + 1), shoulder_name, round(dot, 5)) org_shoulder_diff = (data_set.org_model.bones[arm_name].position - data_set.org_model.bones[shoulder_name].position) + org_shoulder_diff.one() rep_shoulder_diff = (data_set.rep_model.bones[arm_name].position - data_set.rep_model.bones[shoulder_name].position) shoulder_diff_ratio = rep_shoulder_diff / org_shoulder_diff @@ -2212,7 +2987,7 @@ def adjust_shoulder_stance_lr(self, data_set_idx: int, shoulder_p_name: str, sho self.adjust_shoulder_stance_far(data_set_idx, shoulder_p_name, shoulder_name, arm_name, 0, is_shoulder_p) else: logger.info("%s補正: 【No.%s】[%s]のボーン群が、作成元もしくは変換先のいずれかで足りないため、処理をスキップします。", shoulder_name, (data_set_idx + 1), ", ".join(shoulder_target_bones)) - + return True except MKilledException as ke: raise ke @@ -2225,7 +3000,7 @@ def adjust_shoulder_stance_lr(self, data_set_idx: int, shoulder_p_name: str, sho raise e # 肩の傾きが離れている場合のスタンス補正 - def adjust_shoulder_stance_far(self, data_set_idx: int, shoulder_p_name: str, shoulder_name: str, arm_name: str, dot_limit: float, is_shoulder_p: bool): + cdef bint adjust_shoulder_stance_far(self, int data_set_idx, str shoulder_p_name, str shoulder_name, str arm_name, double dot_limit, bint is_shoulder_p): logger.copy(self.options) data_set = self.options.data_set_list[data_set_idx] @@ -2245,14 +3020,15 @@ def adjust_shoulder_stance_far(self, data_set_idx: int, shoulder_p_name: str, sh # 肩幅比率 org_arm_diff = (org_arm_links["左"].get("左腕").position - org_arm_links["右"].get("右腕").position) + org_arm_diff.one() rep_arm_diff = (rep_arm_links["左"].get("左腕").position - rep_arm_links["右"].get("右腕").position) arm_diff_ratio = rep_arm_diff / org_arm_diff arm_diff_ratio.non_zero() # TOの長さ比率(いかり肩ボーンとかあるので、絶対値はとらない) - org_to_diff = (org_arm_links[shoulder_name[0]].get(arm_name).position - org_arm_links[shoulder_name[0]].get("首根元2").position) + org_to_diff = (org_arm_links[shoulder_name[int(0)]].get(arm_name).position - org_arm_links[shoulder_name[int(0)]].get("首根元2").position) org_to_diff.non_zero() - rep_to_diff = (rep_arm_links[shoulder_name[0]].get(arm_name).position - rep_arm_links[shoulder_name[0]].get("首根元2").position) + rep_to_diff = (rep_arm_links[shoulder_name[int(0)]].get(arm_name).position - rep_arm_links[shoulder_name[int(0)]].get("首根元2").position) rep_to_diff.non_zero() to_diff_ratio = rep_to_diff / org_to_diff @@ -2287,9 +3063,9 @@ def adjust_shoulder_stance_far(self, data_set_idx: int, shoulder_p_name: str, sh bf = data_set.motion.calc_bf(shoulder_name, fno) # 腕までのグローバル位置と行列 - org_arm_global_3ds, org_arm_matrixs = MServiceUtils.calc_global_pos(data_set.org_model, org_arm_links[shoulder_name[0]], data_set.org_motion, fno, return_matrix=True) - rep_arm_global_3ds, rep_arm_matrixs = MServiceUtils.calc_global_pos(data_set.rep_model, rep_arm_links[shoulder_name[0]], data_set.motion, fno, return_matrix=True, \ - limit_links=rep_arm_links[shoulder_name[0]].from_links("首根元2")) + org_arm_global_3ds, org_arm_matrixs = MServiceUtils.calc_global_pos(data_set.org_model, org_arm_links[shoulder_name[int(0)]], data_set.org_motion, fno, return_matrix=True) + rep_arm_global_3ds, rep_arm_matrixs = MServiceUtils.calc_global_pos(data_set.rep_model, rep_arm_links[shoulder_name[int(0)]], data_set.motion, fno, return_matrix=True, \ + limit_links=rep_arm_links[shoulder_name[int(0)]].from_links("首根元2")) # 元モデル # 肩のグローバル位置 @@ -2369,8 +3145,22 @@ def adjust_shoulder_stance_far(self, data_set_idx: int, shoulder_p_name: str, sh logger.info("%sスタンス補正: 終了【No.%s】", shoulder_name, (data_set_idx + 1)) + return True + # 肩の傾きが近い場合の肩補正 - def adjust_shoulder_stance_near(self, data_set_idx: int, shoulder_p_name: str, shoulder_name: str, arm_name: str, dot_limit: float, is_shoulder_p: bool): + cdef bint adjust_shoulder_stance_near(self, int data_set_idx, str shoulder_p_name, str shoulder_name, str arm_name, double dot_limit, bint is_shoulder_p): + cdef int fno, fno_idx, prev_fno + cdef double uad + cdef list fnos + cdef dict org_arm_global_3ds, org_arm_links, org_arm_matrixs, rep_arm_global_3ds, rep_arm_links, rep_arm_matrixs + cdef MVector3D arm_diff_ratio, org_arm_diff, org_global_arm_pos, org_global_neck_base_pos, org_local_arm_pos, org_to_diff, recalc_rep_global_arm_pos + cdef MVector3D recalc_rep_local_arm_pos, rep_arm_diff, rep_global_arm_pos, rep_global_neck_base_pos, rep_global_shoulder_pos, rep_local_arm_pos + cdef MVector3D rep_shoulder_slope, rep_to_diff, to_diff_ratio, ratio + cdef VmdBoneFrame bf, org_bf, shoulder_p_bf + cdef MOptionsDataSet data_set + cdef MQuaternion new_shoulder_qq + cdef MMatrix4x4 org_neck_base_matrix, rep_neck_base_matrix + logger.copy(self.options) data_set = self.options.data_set_list[data_set_idx] @@ -2400,15 +3190,15 @@ def adjust_shoulder_stance_near(self, data_set_idx: int, shoulder_p_name: str, s # 肩幅比率 org_arm_diff = (org_arm_links["左"].get("左腕").position - org_arm_links["右"].get("右腕").position) + org_arm_diff.one() rep_arm_diff = (rep_arm_links["左"].get("左腕").position - rep_arm_links["右"].get("右腕").position) arm_diff_ratio = rep_arm_diff / org_arm_diff arm_diff_ratio.one() # 比率なので、0は1に変換する # TOの長さ比率(いかり肩ボーンとかあるので、絶対値はとらない) - org_to_diff = (org_arm_links[shoulder_name[0]].get(arm_name).position - org_arm_links[shoulder_name[0]].get("首根元").position) + org_to_diff = (org_arm_links[shoulder_name[int(0)]].get(arm_name).position - org_arm_links[shoulder_name[int(0)]].get("首根元").position) org_to_diff.one() - rep_to_diff = (rep_arm_links[shoulder_name[0]].get(arm_name).position - rep_arm_links[shoulder_name[0]].get("首根元").position) - rep_to_diff.one() + rep_to_diff = (rep_arm_links[shoulder_name[int(0)]].get(arm_name).position - rep_arm_links[shoulder_name[int(0)]].get("首根元").position) to_diff_ratio = rep_to_diff / org_to_diff logger.test("arm_diff_ratio: %s", arm_diff_ratio) @@ -2422,8 +3212,8 @@ def adjust_shoulder_stance_near(self, data_set_idx: int, shoulder_p_name: str, s initial_dataset = MOptionsDataSet(VmdMotion(), data_set.org_model, data_set.rep_model, data_set.output_vmd_path, data_set.detail_stance_flg, data_set.twist_flg, [], None, 0, []) self.calc_rotation_stance_shoulder(initial_bf, data_set_idx, initial_dataset, \ - org_shoulder_links, org_arm_links[shoulder_name[0]], rep_shoulder_links, \ - rep_arm_links[shoulder_name[0]], org_shoulder_under_links, \ + org_shoulder_links, org_arm_links[shoulder_name[int(0)]], rep_shoulder_links, \ + rep_arm_links[shoulder_name[int(0)]], org_shoulder_under_links, \ shoulder_name, arm_name, ratio, rep_shoulder_initial_slope_qq, MQuaternion(), 0) shoulder_initial_qq = initial_bf.rotation @@ -2456,8 +3246,8 @@ def adjust_shoulder_stance_near(self, data_set_idx: int, shoulder_p_name: str, s shoulder_bf = data_set.motion.calc_bf(shoulder_name, fno) self.calc_rotation_stance_shoulder(shoulder_bf, data_set_idx, data_set, \ - org_shoulder_links, org_arm_links[shoulder_name[0]], rep_shoulder_links, \ - rep_arm_links[shoulder_name[0]], org_shoulder_under_links, \ + org_shoulder_links, org_arm_links[shoulder_name[int(0)]], rep_shoulder_links, \ + rep_arm_links[shoulder_name[int(0)]], org_shoulder_under_links, \ shoulder_name, arm_name, ratio, rep_shoulder_initial_slope_qq, shoulder_initial_qq, dot_limit) # bf登録 @@ -2472,12 +3262,24 @@ def adjust_shoulder_stance_near(self, data_set_idx: int, shoulder_p_name: str, s logger.info("%sスタンス補正: 終了【No.%s】", shoulder_name, (data_set_idx + 1)) + return True + # 肩補正 - def calc_rotation_stance_shoulder(self, bf: VmdBoneFrame, data_set_idx: int, data_set: MOptionsDataSet, \ - org_from_links: BoneLinks, org_to_links: BoneLinks, rep_from_links: BoneLinks, \ - rep_to_links: BoneLinks, org_shoulder_under_links: BoneLinks, \ - from_bone_name: str, to_bone_name: str, ratio: MVector3D, \ - rep_initial_slope_qq: MQuaternion, cancel_qq: MQuaternion, dot_limit): + cdef bint calc_rotation_stance_shoulder(self, VmdBoneFrame bf, int data_set_idx, MOptionsDataSet data_set, \ + BoneLinks org_from_links, BoneLinks org_to_links, BoneLinks rep_from_links, \ + BoneLinks rep_to_links, BoneLinks org_shoulder_under_links, \ + str from_bone_name, str to_bone_name, MVector3D ratio, \ + MQuaternion rep_initial_slope_qq, MQuaternion cancel_qq, dot_limit): + cdef str base_bone_name + cdef double rep_front_to_x, rep_front_to_y, rep_front_to_z, uad + cdef dict new_rep_front_to_global_3ds, org_front_to_global_3ds, org_shoulder_under_global_3ds, org_to_global_3ds + cdef dict rep_front_to_global_3ds, rep_to_global_3ds, rotated_to_3ds + cdef MVector3D direction, new_rep_front_to_pos, new_rep_to_pos, org_front_base_pos, org_front_to_pos, org_shoulder_pos + cdef MVector3D org_shoulder_under_pos, rep_from_pos, rep_front_base_pos, rep_front_to_pos, rep_to_pos, up, up_pos + cdef MQuaternion from_orientation, from_rotation, initial, org_to_direction_qq, rep_to_direction_qq + cdef VmdBoneFrame org_bf + cdef BoneLinks org_limit_links, rep_limit_links + logger.test("f: %s -----------------------------", bf.fno) base_bone_name = "首根元" @@ -2580,8 +3382,14 @@ def calc_rotation_stance_shoulder(self, bf: VmdBoneFrame, data_set_idx: int, dat # 元にもない場合(ないはず)、はそのまま設定 bf.rotation = from_rotation + return True + # 指定したボーンを親ボーンの調整量に合わせてキャンセル - def adjust_rotation_by_parent(self, data_set_idx: int, data_set: MOptionsDataSet, target_bone_name: str, target_parent_name: str): + cdef bint adjust_rotation_by_parent(self, int data_set_idx, MOptionsDataSet data_set, str target_bone_name, str target_parent_name): + cdef int fno + cdef VmdBoneFrame bf, org_parent_bf, rep_parent_bf + cdef MQuaternion org_deformed_qq, rep_deformed_qq + for fno in data_set.motion.get_bone_fnos(target_bone_name): bf = data_set.motion.bones[target_bone_name][fno] @@ -2600,14 +3408,22 @@ def adjust_rotation_by_parent(self, data_set_idx: int, data_set: MOptionsDataSet bf.rotation = rep_parent_bf.rotation.inverted() * org_parent_bf.rotation * bf.rotation + return True + # 指定したボーンを親ボーンの調整量に合わせてキャンセル - def adjust_rotation_by_parent_ik(self, data_set_idx: int, data_set: MOptionsDataSet, target_bone_name: str, target_parent_name: str, target_ik_name: str): + cdef bint adjust_rotation_by_parent_ik(self, int data_set_idx, MOptionsDataSet data_set, str target_bone_name, str target_parent_name, str target_ik_name): + cdef int fno + cdef list fnos, ik_on_fnos + cdef bint is_ik_on + cdef VmdBoneFrame bf, org_parent_bf, rep_parent_bf + cdef MQuaternion org_deformed_qq, rep_deformed_qq + fnos = data_set.motion.get_bone_fnos(target_bone_name) ik_on_fnos = [] if len(fnos) == 0: # 処理対象が1件もなければ終了 - return + return True is_ik_on = True for fno in range(fnos[-1]): @@ -2643,8 +3459,15 @@ def adjust_rotation_by_parent_ik(self, data_set_idx: int, data_set: MOptionsData bf.rotation = rep_parent_bf.rotation.inverted() * org_parent_bf.rotation * bf.rotation + return True + # スタンス用細分化 - def prepare_split_stance(self, data_set_idx: int, data_set: MOptionsDataSet, target_bone_name: str): + cdef bint prepare_split_stance(self, int data_set_idx, MOptionsDataSet data_set, str target_bone_name): + cdef double diff_degree + cdef int fno, fidx, half_fno + cdef list fnos + cdef VmdBoneFrame prev_bf, bf, half_bf + fnos = data_set.motion.get_bone_fnos(target_bone_name) for fidx, fno in enumerate(fnos): @@ -2664,8 +3487,12 @@ def prepare_split_stance(self, data_set_idx: int, data_set: MOptionsDataSet, tar half_bf = data_set.motion.calc_bf(target_bone_name, half_fno) data_set.motion.regist_bf(half_bf, target_bone_name, half_fno) + return True + # 腕スタンス補正 - def adjust_arm_stance(self, data_set_idx: int, data_set: MOptionsDataSet): + cdef bint adjust_arm_stance(self, int data_set_idx, MOptionsDataSet data_set): + cdef dict arm_diff_qq_dic + logger.info("腕スタンス補正 【No.%s】", (data_set_idx + 1), decoration=MLogger.DECORATION_LINE) # 腕のスタンス差 @@ -2674,9 +3501,9 @@ def adjust_arm_stance(self, data_set_idx: int, data_set: MOptionsDataSet): futures = [] with ThreadPoolExecutor(thread_name_prefix="arm{0}".format(data_set_idx), max_workers=self.options.max_workers) as executor: for bone_name in ["左腕", "左ひじ", "左手首", "右腕", "右ひじ", "右手首"]: - futures.append(executor.submit(self.adjust_arm_stance_pool, data_set_idx, arm_diff_qq_dic, bone_name)) + futures.append(executor.submit(self.adjust_arm_stance_pool, self, data_set_idx, arm_diff_qq_dic, bone_name)) for bone_name in ["左腕捩", "左手捩", "右腕捩", "右手捩"]: - futures.append(executor.submit(self.adjust_arm_stance_twist_pool, data_set_idx, bone_name)) + futures.append(executor.submit(self.adjust_arm_stance_twist_pool, self, data_set_idx, bone_name)) concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) for f in futures: @@ -2692,7 +3519,13 @@ def adjust_arm_stance(self, data_set_idx: int, data_set: MOptionsDataSet): return True - def adjust_arm_stance_twist_pool(self, data_set_idx: int, bone_name: str): + cdef bint adjust_arm_stance_twist_pool(self, int data_set_idx, str bone_name): + cdef double degree + cdef MOptionsDataSet data_set + cdef MVector3D axis + cdef VmdBoneFrame bf + cdef MQuaternion rep_qq + try: logger.copy(self.options) data_set = self.options.data_set_list[data_set_idx] @@ -2726,7 +3559,10 @@ def adjust_arm_stance_twist_pool(self, data_set_idx: int, bone_name: str): raise e # 腕スタンス補正左右 - def adjust_arm_stance_pool(self, data_set_idx: int, arm_diff_qq_dic: dict, bone_name: str): + cdef bint adjust_arm_stance_pool(self, int data_set_idx, dict arm_diff_qq_dic, str bone_name): + cdef MOptionsDataSet data_set + cdef VmdBoneFrame bf + try: logger.copy(self.options) data_set = self.options.data_set_list[data_set_idx] @@ -2754,10 +3590,13 @@ def adjust_arm_stance_pool(self, data_set_idx: int, arm_diff_qq_dic: dict, bone_ import traceback logger.error("サイジング処理が意図せぬエラーで終了しました。\n\n%s", traceback.print_exc()) raise e - + # 腕スタンス補正用傾き計算 - def calc_arm_stance(self, data_set: MOptionsDataSet): - arm_diff_qq_dic = {} + cdef dict calc_arm_stance(self, MOptionsDataSet data_set): + cdef dict arm_diff_qq_dic = {} + cdef str from_bone_name, target_bone_name, to_bone_name + cdef list bone_names + cdef MQuaternion org_from_qq, rep_from_qq, org_to_qq, rep_to_qq for direction in ["左", "右"]: for from_bone_type, target_bone_type, to_bone_type in [(None, "腕", "ひじ"), ("腕", "ひじ", "手首"), ("ひじ", "手首", "中指1")]: diff --git a/src/setup_ext.py b/src/setup_ext.py index 47c83ae..0fa89c7 100644 --- a/src/setup_ext.py +++ b/src/setup_ext.py @@ -10,7 +10,8 @@ def get_ext(): ext = [] sources = ["module\\MMath.pyx", "module\\MOptions.pyx", "module\\MParams.pyx", \ "utils\\MLogger.py", "utils\\MBezierUtils.pyx", "utils\\MServiceUtils.pyx", \ - "mmd\\VmdData.pyx", "mmd\\VmdReader.py", "mmd\\PmxData.pyx", "mmd\\PmxReader.py"] + "mmd\\VmdData.pyx", "mmd\\VmdReader.py", "mmd\\PmxData.pyx", "mmd\\PmxReader.py", \ + "service\\parts\\StanceService.pyx", "service\\parts\\ArmAvoidanceService.pyx", "service\\parts\\ArmAlignmentService.pyx"] # for path in glob.glob("*/**/*.pyx", recursive=True): # if os.path.isfile(path): # print(path) diff --git a/src/utils/MLogger.py b/src/utils/MLogger.py index 6a5961a..47569b4 100644 --- a/src/utils/MLogger.py +++ b/src/utils/MLogger.py @@ -248,6 +248,6 @@ def initialize(cls, level=logging.INFO, is_file=False): @cython.ccall def print_message(msg: str, target_level: int): - sys.stdout.write("\n" + msg, (target_level < MLogger.INFO)) + sys.stdout.write(msg + "\n", (target_level < MLogger.INFO)) diff --git a/vmdising_np64.spec b/vmdising_np64.spec index 99ec0e6..1b3c81a 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,7 +26,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β52_64bit', + name='VmdSizing_5.01_β53_64bit', debug=False, bootloader_ignore_signals=False, strip=False, From 23b0bfe180134ed8bf8dbdfa8809652328c40b8e Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Sun, 27 Sep 2020 15:26:42 +0900 Subject: [PATCH 20/37] =?UTF-8?q?5.01=5F=CE=B254?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VmdSizing_5.01_β54 miumiu ・モーフ置換、接触回避  ・ファイルセットが変更されていたら(ハッシュが変わっていたら)処理対象外  ・ワーニングを出す  ・進捗数はわざと足りなくさせて、認知を促す --- src/executor.py | 2 +- src/form/panel/ArmPanel.py | 19 ++++++++--- src/form/panel/MorphPanel.py | 14 +++++--- src/form/worker/SizingWorkerThread.py | 21 +++++++----- src/mmd/VmdData.pxd | 2 +- src/mmd/VmdData.pyx | 2 +- src/service/parts/ArmAvoidanceService.pyx | 41 +++++++++++------------ src/setup.bat | 4 +-- vmdising_np64.spec | 4 +-- 9 files changed, 63 insertions(+), 46 deletions(-) diff --git a/src/executor.py b/src/executor.py index a3c2e91..c518d0a 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00_β53" +VERSION_NAME = "ver5.00_β54" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/form/panel/ArmPanel.py b/src/form/panel/ArmPanel.py index 18f8dd0..92fc27f 100644 --- a/src/form/panel/ArmPanel.py +++ b/src/form/panel/ArmPanel.py @@ -199,11 +199,20 @@ def get_avoidance_target(self): if self.arm_process_flg_avoidance.GetValue() == 0: return target - # 選択された剛体リストを入力欄に設定 - for set_no, set_data in self.avoidance_set_dict.items(): - if set_data.rep_choices: - target[set_no - 1] = [set_data.rep_avoidance_names[n] for n in set_data.rep_choices.GetSelections()] - + # 選択された剛体リストを入力欄に設定(ハッシュが同じ場合のみ) + if 1 in self.avoidance_set_dict and self.avoidance_set_dict[1].rep_choices: + if self.avoidance_set_dict[1].equal_hashdigest(self.frame.file_panel_ctrl.file_set): + target[0] = [self.avoidance_set_dict[1].rep_avoidance_names[n] for n in self.avoidance_set_dict[1].rep_choices.GetSelections()] + else: + logger.warning("【No.%s】接触回避設定後、ファイルセットが変更されたため、接触回避をクリアします", 1, decoration=MLogger.DECORATION_BOX) + + for set_no in list(self.avoidance_set_dict.keys())[1:]: + if set_no in self.avoidance_set_dict and self.avoidance_set_dict[set_no].rep_choices: + if len(self.frame.multi_panel_ctrl.file_set_list) >= set_no - 1 and self.avoidance_set_dict[set_no].equal_hashdigest(self.frame.multi_panel_ctrl.file_set_list[set_no - 2]): + target[set_no - 1] = [self.avoidance_set_dict[set_no].rep_avoidance_names[n] for n in self.avoidance_set_dict[set_no].rep_choices.GetSelections()] + else: + logger.warning("【No.%s】接触回避設定後、ファイルセットが変更されたため、接触回避をクリアします", set_no, decoration=MLogger.DECORATION_BOX) + return target def on_click_avoidance_target(self, event: wx.Event): diff --git a/src/form/panel/MorphPanel.py b/src/form/panel/MorphPanel.py index 99826c9..c5bcb83 100644 --- a/src/form/panel/MorphPanel.py +++ b/src/form/panel/MorphPanel.py @@ -51,13 +51,19 @@ def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int): self.fit() # モーフタブからモーフ置換リスト生成 - def get_morph_list(self, set_no: int): + def get_morph_list(self, set_no: int, vmd_digest: str, org_model_digest: str, rep_model_digest: str): if set_no not in self.morph_set_dict: # そもそも登録がなければ何もなし - return [] + return [], False else: - # あれば、そのNoのモーフ置換リスト - return self.morph_set_dict[set_no].get_morph_list() + morph_set = self.morph_set_dict[set_no] + if morph_set.vmd_digest == vmd_digest and morph_set.org_model_digest == org_model_digest and morph_set.rep_model_digest == rep_model_digest: + # あれば、そのNoのモーフ置換リスト + return morph_set.get_morph_list(), True + else: + logger.warning("【No.%s】モーフ置換設定後、ファイルセットが変更されたため、モーフ置換をクリアします", set_no, decoration=MLogger.DECORATION_BOX) + # ハッシュが一致してない場合空(設定されていた事だけ返す) + return [], True # モーフタブ初期化処理 def initialize(self, event: wx.Event): diff --git a/src/form/worker/SizingWorkerThread.py b/src/form/worker/SizingWorkerThread.py index 9fd97a8..f58433a 100644 --- a/src/form/worker/SizingWorkerThread.py +++ b/src/form/worker/SizingWorkerThread.py @@ -45,7 +45,7 @@ def thread_event(self): now_camera_data = self.frame.camera_panel_ctrl.camera_vmd_file_ctrl.data.copy() now_camera_output_vmd_path = self.frame.camera_panel_ctrl.output_camera_vmd_file_ctrl.file_ctrl.GetPath() - if self.frame.file_panel_ctrl.file_set.motion_vmd_file_ctrl.load(): + if self.frame.file_panel_ctrl.file_set.is_loaded(): proccess_key = "【No.1】{0}({1})".format( \ os.path.basename(self.frame.file_panel_ctrl.file_set.motion_vmd_file_ctrl.data.path), \ @@ -81,8 +81,10 @@ def thread_event(self): if self.frame.arm_panel_ctrl.arm_process_flg_avoidance.GetValue() > 0: self.frame.file_panel_ctrl.tree_process_dict[proccess_key]["接触回避"] = False - total_process += min(1, len(self.frame.morph_panel_ctrl.get_morph_list(1))) # モーフ置換 - if len(self.frame.morph_panel_ctrl.get_morph_list(1)) > 0: + morph_list, morph_seted = self.frame.morph_panel_ctrl.get_morph_list(1, self.frame.file_panel_ctrl.file_set.motion_vmd_file_ctrl.data.digest, \ + self.frame.file_panel_ctrl.file_set.org_model_file_ctrl.data.digest, self.frame.file_panel_ctrl.file_set.rep_model_file_ctrl.data.digest) # noqa + if morph_seted: + total_process += 1 # モーフ置換 self.frame.file_panel_ctrl.tree_process_dict[proccess_key]["モーフ置換"] = False # 1件目は必ず読み込む @@ -93,7 +95,7 @@ def thread_event(self): output_vmd_path=self.frame.file_panel_ctrl.file_set.output_vmd_file_ctrl.file_ctrl.GetPath(), \ detail_stance_flg=self.frame.file_panel_ctrl.file_set.org_model_file_ctrl.title_parts_ctrl.GetValue(), \ twist_flg=self.frame.file_panel_ctrl.file_set.rep_model_file_ctrl.title_parts_ctrl.GetValue(), \ - morph_list=self.frame.morph_panel_ctrl.get_morph_list(1), \ + morph_list=morph_list, \ camera_org_model=camera_org_model, \ camera_offset_y=camera_offset_y, \ selected_stance_details=self.frame.file_panel_ctrl.file_set.get_selected_stance_details() @@ -129,11 +131,12 @@ def thread_event(self): if self.frame.arm_panel_ctrl.arm_process_flg_avoidance.GetValue() > 0: self.frame.file_panel_ctrl.tree_process_dict[proccess_key]["接触回避"] = False - total_process += min(1, len(self.frame.morph_panel_ctrl.get_morph_list(file_set.set_no))) # モーフ置換 - - if len(self.frame.morph_panel_ctrl.get_morph_list(file_set.set_no)) > 0: + morph_list, morph_seted = self.frame.morph_panel_ctrl.get_morph_list(file_set.set_no, self.frame.file_panel_ctrl.file_set.motion_vmd_file_ctrl.data.digest, \ + self.frame.file_panel_ctrl.file_set.org_model_file_ctrl.data.digest, self.frame.file_panel_ctrl.file_set.rep_model_file_ctrl.data.digest) # noqa + if morph_seted: + total_process += 1 # モーフ置換 self.frame.file_panel_ctrl.tree_process_dict[proccess_key]["モーフ置換"] = False - + camera_offset_y = 0 camera_org_model = file_set.org_model_file_ctrl.data if multi_idx + 2 in self.frame.camera_panel_ctrl.camera_set_dict: @@ -149,7 +152,7 @@ def thread_event(self): output_vmd_path=file_set.output_vmd_file_ctrl.file_ctrl.GetPath(), \ detail_stance_flg=file_set.org_model_file_ctrl.title_parts_ctrl.GetValue(), \ twist_flg=file_set.rep_model_file_ctrl.title_parts_ctrl.GetValue(), \ - morph_list=self.frame.morph_panel_ctrl.get_morph_list(file_set.set_no), \ + morph_list=morph_list, \ camera_org_model=camera_org_model, \ camera_offset_y=camera_offset_y, \ selected_stance_details=file_set.get_selected_stance_details() diff --git a/src/mmd/VmdData.pxd b/src/mmd/VmdData.pxd index ec690cc..0e3cdee 100644 --- a/src/mmd/VmdData.pxd +++ b/src/mmd/VmdData.pxd @@ -86,7 +86,7 @@ cdef class VmdMotion: cdef reset_interpolation(self, str target_bone_name, VmdBoneFrame prev_bf, VmdBoneFrame now_bf, VmdBoneFrame next_bf, \ list before_bz, list after_bz, list x1_idxs, list y1_idxs, list x2_idxs, list y2_idxs) - cdef copy_interpolation(self, VmdBoneFrame org_bf, VmdBoneFrame rep_bf, str bz_type) + cpdef copy_interpolation(self, VmdBoneFrame org_bf, VmdBoneFrame rep_bf, str bz_type) cdef reset_interpolation_parts(self, str target_bone_name, VmdBoneFrame bf, list bzs, list x1_idxs, list y1_idxs, list x2_idxs, list y2_idxs) diff --git a/src/mmd/VmdData.pyx b/src/mmd/VmdData.pyx index 3b2fac8..fc413b9 100644 --- a/src/mmd/VmdData.pyx +++ b/src/mmd/VmdData.pyx @@ -1111,7 +1111,7 @@ cdef class VmdMotion: self.reset_interpolation_parts(target_bone_name, next_bf, after_bz, x1_idxs, y1_idxs, x2_idxs, y2_idxs) # 補間曲線のコピー - cdef copy_interpolation(self, VmdBoneFrame org_bf, VmdBoneFrame rep_bf, str bz_type): + cpdef copy_interpolation(self, VmdBoneFrame org_bf, VmdBoneFrame rep_bf, str bz_type): cdef list bz_x1_idxs, bz_y1_idxs, bz_x2_idxs, bz_y2_idxs cdef list org_interpolation = cPickle.loads(cPickle.dumps(org_bf.interpolation, -1)) diff --git a/src/service/parts/ArmAvoidanceService.pyx b/src/service/parts/ArmAvoidanceService.pyx index 9dde672..2f0bfe2 100644 --- a/src/service/parts/ArmAvoidanceService.pyx +++ b/src/service/parts/ArmAvoidanceService.pyx @@ -72,35 +72,34 @@ cdef class ArmAvoidanceService: self.avoidance_options = {} for data_set_idx, data_set in enumerate(self.options.data_set_list): - logger.info("接触回避 【No.%s】", (data_set_idx + 1), decoration=MLogger.DECORATION_LINE) + if data_set_idx in self.target_data_set_idxs: + logger.info("接触回避 【No.%s】", (data_set_idx + 1), decoration=MLogger.DECORATION_LINE) - # 接触回避用準備 - self.avoidance_options[(data_set_idx, "左")] = self.prepare_avoidance(data_set_idx, "左") - self.avoidance_options[(data_set_idx, "右")] = self.prepare_avoidance(data_set_idx, "右") + # 接触回避用準備 + self.avoidance_options[(data_set_idx, "左")] = self.prepare_avoidance(data_set_idx, "左") + self.avoidance_options[(data_set_idx, "右")] = self.prepare_avoidance(data_set_idx, "右") - for data_set_idx, data_set in enumerate(self.options.data_set_list): - self.execute_avoidance_pool(data_set_idx, "右") - self.execute_avoidance_pool(data_set_idx, "左") + futures = [] + with ThreadPoolExecutor(thread_name_prefix="avoidance", max_workers=self.options.max_workers) as executor: + for data_set_idx, data_set in enumerate(self.options.data_set_list): + if data_set_idx in self.target_data_set_idxs: + futures.append(executor.submit(self.execute_avoidance_pool, data_set_idx, "右")) + futures.append(executor.submit(self.execute_avoidance_pool, data_set_idx, "左")) + + concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) + + for f in futures: + if not f.result(): + return False - if self.options.now_process_ctrl: + for data_set_idx, data_set in enumerate(self.options.data_set_list): + if data_set_idx in self.target_data_set_idxs and self.options.now_process_ctrl: self.options.now_process += 1 self.options.now_process_ctrl.write(str(self.options.now_process)) proccess_key = "【No.{0}】{1}({2})".format(data_set_idx + 1, os.path.basename(data_set.motion.path), data_set.rep_model.name) self.options.tree_process_dict[proccess_key]["接触回避"] = True - # futures = [] - # with ThreadPoolExecutor(thread_name_prefix="avoidance", max_workers=self.options.max_workers) as executor: - # for data_set_idx, data_set in enumerate(self.options.data_set_list): - # futures.append(executor.submit(self.execute_avoidance_pool, data_set_idx, "右")) - # futures.append(executor.submit(self.execute_avoidance_pool, data_set_idx, "左")) - - # concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) - - # for f in futures: - # if not f.result(): - # return False - return True # 接触回避 @@ -796,6 +795,6 @@ cdef class ArmAvoidanceService: and data_set_idx not in target_data_set_idxs and data_set_idx in self.options.arm_options.avoidance_target_list: # ボーンセットがあり、腕系サイジング可能で、かつまだ登録されていない場合 target_data_set_idxs.append(data_set_idx) - + return target_data_set_idxs diff --git a/src/setup.bat b/src/setup.bat index 88b4c05..28749f3 100644 --- a/src/setup.bat +++ b/src/setup.bat @@ -4,10 +4,10 @@ cls cd /d %~dp0 rem -- svt@Cp -kernprof -l setup.py build_ext --inplace +rem kernprof -l setup.py build_ext --inplace rem -- ʏp -rem python setup.py build_ext --inplace +python setup.py build_ext --inplace cd .. diff --git a/vmdising_np64.spec b/vmdising_np64.spec index 1b3c81a..4178a2b 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,12 +26,12 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β53_64bit', + name='VmdSizing_5.01_β54_64bit', debug=False, bootloader_ignore_signals=False, strip=False, upx=True, runtime_tmpdir=None, - console=True, + console=False, icon='.\\src\\vmdsizing.ico') From 470ab85db25c8886359137b0ca559eb8a52affb2 Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Sun, 27 Sep 2020 18:46:07 +0900 Subject: [PATCH 21/37] =?UTF-8?q?5.01=5F=CE=B255?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VmdSizing_5.01_β55 miumiu ・スムージング  ・不要キー削除処理を全体的に修正 ・サイジング  ・全処理終了した後に、処理数が合わなかった場合、警告出力 ・ファイルタブ  ・ファイルセットが変更された場合、カメラ出力パスも強制再出力するよう修正 --- smooth.bat | 6 ++-- src/executor.py | 2 +- src/form/panel/FilePanel.py | 2 +- src/mmd/VmdData.pyx | 12 +++++-- src/service/ConvertSmoothService.py | 55 ++++++++--------------------- src/service/SizingService.py | 4 ++- vmdising_np64.spec | 2 +- 7 files changed, 33 insertions(+), 50 deletions(-) diff --git a/smooth.bat b/smooth.bat index 39778a4..fbe2a73 100644 --- a/smooth.bat +++ b/smooth.bat @@ -8,16 +8,16 @@ cd /d %~dp0 cls -set MOTION_PATH="D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_1l\tIII Ȃ‚\nac_aikotoba3_300-468_Ԏ~N_W_T_20200917_222601.vmd" +set MOTION_PATH="D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_1l\tIII Ȃ‚\nac_aikotoba3_300-468_Ԏ~N_W_T_20200927_161532.vmd" set MODEL_PATH=D:\MMD\MikuMikuDance_v926x64\UserFile\Model\VOCALOID\~N\Ԏ~N\Ԏ~N_W.pmx -set LOOP_CNT=2 +set LOOP_CNT=5 set INTERPOLATION=0 set BONE_LIST="Er;Er;EЂ;E蝀;E;r;r;Ђ;蝀;;" rem set BONE_LIST="EЂ" rem set BONE_LIST="Er" rem set BONE_LIST="" -set VERBOSE="10" +set VERBOSE="20" activate vmdsizing_cython && src\setup.bat && python src/executor_smooth.py --motion_path %MOTION_PATH% --model_path %MODEL_PATH% --loop_cnt %LOOP_CNT% --interpolation %INTERPOLATION% --bone_list %BONE_LIST% --verbose %VERBOSE% diff --git a/src/executor.py b/src/executor.py index c518d0a..732271e 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00_β54" +VERSION_NAME = "ver5.00_β55" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/form/panel/FilePanel.py b/src/form/panel/FilePanel.py index 3a94377..f34c9d9 100644 --- a/src/form/panel/FilePanel.py +++ b/src/form/panel/FilePanel.py @@ -230,7 +230,7 @@ def on_exec(self, event: wx.Event): def set_output_vmd_path(self, event, is_force=False): self.file_set.set_output_vmd_path(event, is_force) # カメラ出力パスも一緒に変更する - self.frame.camera_panel_ctrl.header_panel.set_output_vmd_path(event) + self.frame.camera_panel_ctrl.header_panel.set_output_vmd_path(event, is_force) def save(self): diff --git a/src/mmd/VmdData.pyx b/src/mmd/VmdData.pyx index fc413b9..43ae3bd 100644 --- a/src/mmd/VmdData.pyx +++ b/src/mmd/VmdData.pyx @@ -785,7 +785,6 @@ cdef class VmdMotion: else: # キーの結合に失敗して、かつ変曲点がない場合、前回結合できたキーフレの次に置き換える logger.debug("キーの結合に失敗して、かつ変曲点がない場合") - last_fno += 1 is_inflection = True if fno // 100 > prev_sep_fno and fnos[-1] > 0: @@ -817,6 +816,8 @@ cdef class VmdMotion: # 開始キーフレは、変曲点までずらす start_fno = last_fno + (0 if last_fno > start_fno else 1) + # 最終結合キーフレを変曲点までずらす + last_fno = start_fno # fnoを変曲点まで戻す fno = start_fno @@ -842,7 +843,7 @@ cdef class VmdMotion: for f in range(1, fnos[-1] + 1): if f in self.bones[bone_name]: del self.bones[bone_name][f] - + if r_start_fno < 0 and r_end_fno < 0: # 範囲指定がない場合、全範囲 active_fnos = self.get_bone_fnos(bone_name, is_key=True) @@ -850,8 +851,13 @@ cdef class VmdMotion: # 範囲指定がある場合はその範囲内だけ active_fnos = self.get_bone_fnos(bone_name, start_fno=r_start_fno, end_fno=r_end_fno, is_key=True) + for fno in range(fnos[-1]): + if fno in self.bones[bone_name] and fno not in active_fnos: + # 最後に物理削除 + del self.bones[bone_name][fno] + logger.debug("remove_unnecessary_bf after: %s, %s, all: %s", bone_name, active_fnos, len(fnos)) - + # 補間曲線分割ありで登録 def regist_bf(self, bf: VmdBoneFrame, bone_name: str, fno: int, copy_interpolation=False): self.c_regist_bf(bf, bone_name, fno, copy_interpolation) diff --git a/src/service/ConvertSmoothService.py b/src/service/ConvertSmoothService.py index 58468a9..06287c3 100644 --- a/src/service/ConvertSmoothService.py +++ b/src/service/ConvertSmoothService.py @@ -92,19 +92,6 @@ def convert_smooth(self): for f in futures: if not f.result(): return False - - # 処理回数が3回以上の場合、フィルタをかける - if self.options.loop_cnt >= 3: - futures = [] - with ThreadPoolExecutor(thread_name_prefix="filter", max_workers=self.options.max_workers) as executor: - for bone_name in self.options.motion.bones.keys(): - if bone_name in self.options.model.bones and bone_name in self.options.bone_list: - futures.append(executor.submit(self.fitering, bone_name)) - concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) - - for f in futures: - if not f.result(): - return False # 処理回数が2回以上の場合、不要キー削除 if self.options.loop_cnt >= 2: @@ -112,7 +99,7 @@ def convert_smooth(self): with ThreadPoolExecutor(thread_name_prefix="remove", max_workers=self.options.max_workers) as executor: for bone_name in self.options.motion.bones.keys(): if bone_name in self.options.model.bones and bone_name in self.options.bone_list: - futures.append(executor.submit(self.remove_unnecessary_bf, bone_name)) + futures.append(executor.submit(self.remove_filterd_bf, bone_name)) concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) for f in futures: @@ -122,14 +109,23 @@ def convert_smooth(self): return True # 不要キー削除処理 - def remove_unnecessary_bf(self, bone_name: str): + def remove_filterd_bf(self, bone_name: str): try: logger.copy(self.options) - logger.info("【不要キー削除】%s 開始", bone_name) - self.options.motion.remove_unnecessary_bf(0, bone_name, self.options.model.bones[bone_name].getRotatable(), \ - self.options.model.bones[bone_name].getTranslatable(), offset=(self.options.loop_cnt - 2)) - logger.info("【不要キー削除】%s 終了", bone_name) + for n in range(1, self.options.loop_cnt): + # 処理回数が3回以上の場合、フィルタをかける + if self.options.loop_cnt > 2: + logger.info("【フィルタリング%s回目】%s 開始", n - 1, bone_name) + self.options.motion.smooth_filter_bf(0, bone_name, self.options.model.bones[bone_name].getRotatable(), \ + self.options.model.bones[bone_name].getTranslatable(), \ + config={"freq": 30, "mincutoff": 0.1, "beta": 0.1, "dcutoff": 1}) + logger.info("【フィルタリング%s回目】%s 終了", n - 1, bone_name) + + logger.info("【不要キー削除%s回目】%s 開始", n, bone_name) + self.options.motion.remove_unnecessary_bf(0, bone_name, self.options.model.bones[bone_name].getRotatable(), \ + self.options.model.bones[bone_name].getTranslatable(), offset=0) + logger.info("【不要キー削除%s回目】%s 終了", n, bone_name) return True except SizingException as se: @@ -139,27 +135,6 @@ def remove_unnecessary_bf(self, bone_name: str): logger.error("スムージング処理が意図せぬエラーで終了しました。", e) return False - # フィルタリング処理 - def fitering(self, bone_name: str): - try: - logger.copy(self.options) - - for n in range(1, self.options.loop_cnt - 1): - logger.info("【フィルタリング%s回目】%s 開始", n, bone_name) - self.options.motion.smooth_filter_bf(0, bone_name, self.options.model.bones[bone_name].getRotatable(), \ - self.options.model.bones[bone_name].getTranslatable(), \ - config={"freq": 30, "mincutoff": 0.1, "beta": 0.1, "dcutoff": 1}) - - logger.info("【フィルタリング】%s 終了", bone_name) - - return True - except SizingException as se: - logger.error("スムージング処理が処理できないデータで終了しました。\n\n%s", se.message) - return False - except Exception as e: - logger.error("スムージング処理が意図せぬエラーで終了しました。", e) - return False - # 線形補間で全打ち def prepare_linear(self, bone_name: str): try: diff --git a/src/service/SizingService.py b/src/service/SizingService.py index acc387e..8c874ea 100644 --- a/src/service/SizingService.py +++ b/src/service/SizingService.py @@ -130,7 +130,6 @@ def execute(self): Path(data_set.output_vmd_path).resolve(True) logger.info("【No.%s】 出力終了: %s", (data_set_idx + 1), os.path.basename(data_set.output_vmd_path), decoration=MLogger.DECORATION_BOX, title="サイジング成功") - except FileNotFoundError as fe: logger.error("【No.%s】出力VMDファイルが正常に作成されなかったようです。\nパスを確認してください。%s\n\n%s", (data_set_idx + 1), data_set.output_vmd_path, fe, decoration=MLogger.DECORATION_BOX) @@ -148,6 +147,9 @@ def execute(self): except FileNotFoundError as fe: logger.error("カメラ出力VMDファイルが正常に作成されなかったようです。\nパスを確認してください。%s\n\n%s", self.options.camera_output_vmd_path, fe, decoration=MLogger.DECORATION_BOX) + if int(self.options.total_process) != int(self.options.now_process): + logger.warning("一部処理がスキップされました。\n画面左下の進捗数をクリックすると、スキップされた処理がグレーで表示されています。", decoration=MLogger.DECORATION_BOX) + return True except MKilledException: return False diff --git a/vmdising_np64.spec b/vmdising_np64.spec index 4178a2b..b66477e 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,7 +26,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β54_64bit', + name='VmdSizing_5.01_β55_64bit', debug=False, bootloader_ignore_signals=False, strip=False, From b40ce42840312053ef818dc0d50281a4fc13220a Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Fri, 23 Oct 2020 03:04:49 +0900 Subject: [PATCH 22/37] =?UTF-8?q?5.01=5F=CE=B256?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VmdSizing_5.01_β56 miumiu ・カメラ  ・距離可動範囲限定機能追加 ・スムージング  ・不要キー削除処理を、一旦5.00リリース時に戻した --- .vscode/launch.json | 13 + smooth.bat | 12 +- src/executor.py | 2 +- src/form/panel/CameraPanel.py | 28 +- src/form/worker/SizingWorkerThread.py | 1 + src/mmd/VmdData.pxd | 2 +- src/mmd/VmdData.pyx | 491 +++++++++++++++----------- src/module/MMath.pyx | 10 +- src/module/MOptions.pxd | 1 + src/module/MOptions.pyx | 3 +- src/service/ConvertSmoothService.py | 8 +- src/service/SizingService.py | 4 +- src/service/parts/CameraService.py | 12 +- src/utils/MBezierUtils.pxd | 2 +- src/utils/MBezierUtils.pyx | 194 ++++++++-- src/utils/MFileutils.py | 7 +- vmdising_np64.spec | 2 +- 17 files changed, 532 insertions(+), 260 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index ed9504f..9a96b34 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -226,6 +226,19 @@ // "--out_log", ] }, + { + "name": "exec_supporter_gui", + "type": "python", + "request": "launch", + "program": "${workspaceFolder}/../motion_supporter/src/executor.py", + "console": "integratedTerminal", + "pythonPath": "${command:python.interpreterPath}", + "stopOnEntry": false, + "args": [ + "--verbose", "10", + "--out_log", "1", + ] + }, { "name": "exec_thread", "type": "python", diff --git a/smooth.bat b/smooth.bat index fbe2a73..2736c70 100644 --- a/smooth.bat +++ b/smooth.bat @@ -16,9 +16,19 @@ set BONE_LIST=" rem set BONE_LIST="EЂ" rem set BONE_LIST="Er" rem set BONE_LIST="" - set VERBOSE="20" +set MOTION_PATH="D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_1l\h}cM[ motion zzp moka\h}cM[_0-1000_Tda~N_ftHver_iD_20200930_074119.vmd" +set MODEL_PATH="D:\MMD\MikuMikuDance_v926x64\UserFile\Model\VOCALOID\~N\TdaftH~N_ver1.1 q\Tda~N_ftHver_i.pmx" +set LOOP_CNT=2 +set INTERPOLATION=0 +set BONE_LIST="ErRY" +rem set BONE_LIST="ERX;ERY;ERZ;ErRX;ErRY;ErRZ;Er;EЂRX;EЂRY;EЂRZ;E蝀;E;r;r;Ђ;蝀;;" +rem set BONE_LIST="EЂ" +rem set BONE_LIST="Er" +rem set BONE_LIST="" +set VERBOSE="10" + activate vmdsizing_cython && src\setup.bat && python src/executor_smooth.py --motion_path %MOTION_PATH% --model_path %MODEL_PATH% --loop_cnt %LOOP_CNT% --interpolation %INTERPOLATION% --bone_list %BONE_LIST% --verbose %VERBOSE% diff --git a/src/executor.py b/src/executor.py index 732271e..20d11a9 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00_β55" +VERSION_NAME = "ver5.00_β56" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/form/panel/CameraPanel.py b/src/form/panel/CameraPanel.py index 6c56a79..f29bd90 100644 --- a/src/form/panel/CameraPanel.py +++ b/src/form/panel/CameraPanel.py @@ -7,6 +7,7 @@ from form.parts.SizingFileSet import SizingFileSet from form.parts.BaseFilePickerCtrl import BaseFilePickerCtrl from form.parts.HistoryFilePickerCtrl import HistoryFilePickerCtrl +from form.parts.FloatSliderCtrl import FloatSliderCtrl from utils import MFileUtils from utils.MLogger import MLogger # noqa @@ -41,6 +42,26 @@ def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int): is_aster=False, is_save=True, set_no=1) self.header_sizer.Add(self.output_camera_vmd_file_ctrl.sizer, 1, wx.EXPAND, 0) + # カメラ距離調整スライダー + self.camera_length_sizer = wx.BoxSizer(wx.HORIZONTAL) + + self.camera_length_txt = wx.StaticText(self.header_panel, wx.ID_ANY, u"距離調整可動範囲", wx.DefaultPosition, wx.DefaultSize, 0) + self.camera_length_txt.SetToolTip(u"カメラの距離の調整範囲を限定したい場合、指定して下さい。\n例えば「2」の場合、オリジナルカメラの「1/2」倍から「2」倍の範囲内でのみ距離を調整します。" \ + + "\nデフォルトの「5」だと、可動範囲無制限で「モデルの映り具合が同じになるように」最大限調整します。") + self.camera_length_txt.Wrap(-1) + self.camera_length_sizer.Add(self.camera_length_txt, 0, wx.ALL, 5) + + self.camera_length_label = wx.StaticText(self.header_panel, wx.ID_ANY, u"(5)", wx.DefaultPosition, wx.DefaultSize, 0) + self.camera_length_label.SetToolTip(u"現在指定されているカメラ距離の可動範囲です。") + self.camera_length_label.Wrap(-1) + self.camera_length_sizer.Add(self.camera_length_label, 0, wx.ALL, 5) + + self.camera_length_slider = FloatSliderCtrl(self.header_panel, wx.ID_ANY, 5, 1, 5, 0.01, self.camera_length_label, wx.DefaultPosition, wx.DefaultSize, wx.SL_HORIZONTAL) + self.camera_length_slider.Bind(wx.EVT_SCROLL_CHANGED, self.set_output_vmd_path) + self.camera_length_sizer.Add(self.camera_length_slider, 1, wx.ALL | wx.EXPAND, 5) + + self.header_sizer.Add(self.camera_length_sizer, 0, wx.ALL | wx.EXPAND, 5) + self.header_panel.SetSizer(self.header_sizer) self.header_panel.Layout() self.sizer.Add(self.header_panel, 0, wx.EXPAND | wx.ALL, 5) @@ -60,6 +81,10 @@ def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int): self.sizer.Add(self.scrolled_window, 1, wx.ALL | wx.EXPAND | wx.FIXED_MINSIZE, 5) self.sizer.Layout() self.fit() + + def set_output_vmd_path(self, event, is_force=False): + # カメラ出力パスを強制的に変更する + self.header_panel.set_output_vmd_path(event, True) # カメラタブからカメラリスト生成 def get_camera_list(self, set_no: int): @@ -139,7 +164,8 @@ def set_output_vmd_path(self, event, is_force=False): output_camera_vmd_path = MFileUtils.get_output_camera_vmd_path( self.parent.camera_vmd_file_ctrl.file_ctrl.GetPath(), self.frame.file_panel_ctrl.file_set.rep_model_file_ctrl.file_ctrl.GetPath(), - self.parent.output_camera_vmd_file_ctrl.file_ctrl.GetPath(), is_force) + self.parent.output_camera_vmd_file_ctrl.file_ctrl.GetPath(), + self.parent.camera_length_slider.GetValue(), is_force) self.parent.output_camera_vmd_file_ctrl.file_ctrl.SetPath(output_camera_vmd_path) diff --git a/src/form/worker/SizingWorkerThread.py b/src/form/worker/SizingWorkerThread.py index f58433a..824e918 100644 --- a/src/form/worker/SizingWorkerThread.py +++ b/src/form/worker/SizingWorkerThread.py @@ -185,6 +185,7 @@ def thread_event(self): ), \ camera_motion=now_camera_data, \ camera_output_vmd_path=now_camera_output_vmd_path, \ + camera_length=self.frame.camera_panel_ctrl.camera_length_slider.GetValue(), \ monitor=self.frame.file_panel_ctrl.console_ctrl, \ is_file=False, \ outout_datetime=logger.outout_datetime, \ diff --git a/src/mmd/VmdData.pxd b/src/mmd/VmdData.pxd index 0e3cdee..34c7d6b 100644 --- a/src/mmd/VmdData.pxd +++ b/src/mmd/VmdData.pxd @@ -57,7 +57,7 @@ cdef class VmdMotion: cdef public list showiks cdef public str digest - cdef c_regist_full_bf(self, int data_set_no, list bone_name_list, int offset) + cdef c_regist_full_bf(self, int data_set_no, list bone_name_list, int offset, bint is_key) cdef list c_get_differ_fnos(self, int data_set_no, list bone_name_list, double limit_degrees, double limit_length) diff --git a/src/mmd/VmdData.pyx b/src/mmd/VmdData.pyx index 43ae3bd..dd8885b 100644 --- a/src/mmd/VmdData.pyx +++ b/src/mmd/VmdData.pyx @@ -302,10 +302,10 @@ cdef class VmdMotion: # ハッシュ値 self.digest = None - def regist_full_bf(self, data_set_no: int, bone_name_list: list, offset=1): - self.c_regist_full_bf(data_set_no, bone_name_list, offset) + def regist_full_bf(self, data_set_no: int, bone_name_list: list, offset=1, is_key=True): + self.c_regist_full_bf(data_set_no, bone_name_list, offset, is_key) - cdef c_regist_full_bf(self, int data_set_no, list bone_name_list, int offset): + cdef c_regist_full_bf(self, int data_set_no, list bone_name_list, int offset, bint is_key): # 指定された全部のボーンのキーフレ取得 cdef list fnos = self.get_bone_fnos(*bone_name_list) @@ -329,6 +329,10 @@ cdef class VmdMotion: bf = self.c_calc_bf(bone_name, fno, is_key=False, is_read=False, is_reset_interpolation=False) self.c_regist_bf(bf, bone_name, fno, copy_interpolation=False) + if not is_key and not bf.read: + # 無効化のままの場合、キーをOFFにしておく + self.bones[bone_name][fno].key = False + if fno // 500 > prev_sep_fno and fnos[-1] > 0: if data_set_no == 0: logger.info("-- %sフレーム目:終了(%s%)【全打ち - %s】", fno, round((fno / fnos[-1]) * 100, 3), bone_name) @@ -545,11 +549,130 @@ cdef class VmdMotion: del self.bones[bone_name][fno] # 指定ボーンの不要キーを削除する - # 変曲点を求める - # https://teratail.com/questions/162391 - def remove_unnecessary_bf(self, data_set_no: int, bone_name: str, is_rot: bint, is_mov: bint, \ - offset=0, rot_diff_limit=0.1, mov_diff_limit=0.1, start_fno=-1, end_fno=-1, is_show_log=True, is_force=False): - self.c_remove_unnecessary_bf(data_set_no, bone_name, is_rot, is_mov, offset, rot_diff_limit, mov_diff_limit, start_fno, end_fno, is_show_log, is_force) + def remove_unnecessary_bf(self, data_set_no: int, bone_name: str, is_rot: bool, is_mov: bool, offset=0, start_fno=-1, end_fno=-1, is_show_log=True, is_force=False): + prev_sep_fno = 0 + + # キーフレを取得する + if start_fno < 0 and end_fno < 0: + # 範囲指定がない場合、全範囲 + fnos = self.get_bone_fnos(bone_name) + else: + # 範囲指定がある場合はその範囲内だけ + fnos = self.get_bone_fnos(bone_name, start_fno=start_fno, end_fno=end_fno) + logger.debug("remove_unnecessary_bf prev: %s, %s", bone_name, len(fnos)) + + if len(fnos) > 2: + sfno = fnos[0] # 開始フレーム番号 + fno = fnos[1] # 次のフレーム番号 + fill_bfs = [] + for fidx, fno in enumerate(fnos): + prev_bf = self.calc_bf(bone_name, sfno) # 繋ぐ元のbf + now_bf = self.calc_bf(bone_name, fno) # 繋ぐ対象のbf + next_bf = self.calc_bf(bone_name, fno + 1) # 繋ぐ先のbf + is_next_key = next_bf.key # nextの有効有無 + + # 一旦登録 + self.regist_bf(now_bf, bone_name, fno) + + # 読み込みキーではない場合、結合を試す + logger.test("now: %s", now_bf) + + if (not now_bf.read or is_force) and fidx > 0: + # 現在キーを追加 + fill_bfs.append(now_bf) + + if self.join_bf(prev_bf, fill_bfs, next_bf, is_rot, is_mov, offset): + # 全ての補間曲線が繋ぐのに成功した場合、繋ぐ + logger.debug("f: %s, %s, ○補間曲線結合", fno, bone_name) + + # nowキーを物理的に削除 + if fno in self.bones[bone_name]: + del self.bones[bone_name][fno] + + # nextキーをキーの有効有無は問わずに登録 + self.regist_bf(next_bf, bone_name, fno + 1) + self.bones[bone_name][fno + 1].key = is_next_key + else: + logger.debug("f: %s, %s, ×補間曲線結合失敗", fno, bone_name) + # どれか失敗してたら、そのまま残す + + # nowキーを有効にする + now_bf.key = True + + sfno = fno # 開始を現在フレーム + fill_bfs = [] # 中間キーをクリア + else: + logger.debug("f: %s, %s, ▲読み込みキー", fno, bone_name) + # 読み込み時のキーである場合、強制的に残す + sfno = fno # 開始を現在フレーム + fill_bfs = [] # 中間キーをクリア + + if fno // 100 > prev_sep_fno: + if data_set_no == 0: + logger.info("-- %sフレーム目:終了(%s%)【不要キー削除 - %s】", fno, round((fno / fnos[-1]) * 100, 3), bone_name) + else: + logger.info("-- %sフレーム目:終了(%s%)【No.%s - 不要キー削除 - %s】", fno, round((fno / fnos[-1]) * 100, 3), data_set_no, bone_name) + + prev_sep_fno = fno // 100 + + if start_fno < 0 and end_fno < 0: + # 範囲指定がない場合、全範囲 + active_fnos = self.get_bone_fnos(bone_name) + else: + # 範囲指定がある場合はその範囲内だけ + active_fnos = self.get_bone_fnos(bone_name, start_fno=start_fno, end_fno=end_fno) + + logger.debug("remove_unnecessary_bf after: %s, %s, all: %s", bone_name, active_fnos, len(fnos)) + + # 補間曲線込みでbfを結合できる場合、結合する + def join_bf(self, prev_bf: VmdBoneFrame, fill_bfs: list, next_bf: VmdBoneFrame, is_rot: bool, is_mov: bool, offset=0): + rot_values = [] + x_values = [] + y_values = [] + z_values = [] + + if is_rot: + rot_values = np.array([prev_bf.rotation.toDegree() * np.sign(prev_bf.rotation.x())] \ + + [bf.rotation.toDegree() * np.sign(bf.rotation.x()) for bf in fill_bfs] \ + + [next_bf.rotation.toDegree() * np.sign(next_bf.rotation.x())]) - (prev_bf.rotation.toDegree() * np.sign(next_bf.rotation.x())) + + logger.test("f: %s, %s, rot_values: %s", prev_bf.fno, prev_bf.name, rot_values) + + if is_mov: + # X ------------ + x_values = np.array([prev_bf.position.x()] + [bf.position.x() for bf in fill_bfs] + [next_bf.position.x()]) - prev_bf.position.x() + + # Y ----------- + y_values = np.array([prev_bf.position.y()] + [bf.position.y() for bf in fill_bfs] + [next_bf.position.y()]) - prev_bf.position.y() + + # Z ----------- + z_values = np.array([prev_bf.position.z()] + [bf.position.z() for bf in fill_bfs] + [next_bf.position.z()]) - prev_bf.position.z() + + logger.test("f: %s, %s, x_values: %s", prev_bf.fno, prev_bf.name, x_values) + logger.test("f: %s, %s, y_values: %s", prev_bf.fno, prev_bf.name, y_values) + logger.test("f: %s, %s, z_values: %s", prev_bf.fno, prev_bf.name, z_values) + + # 結合したベジェ曲線 + joined_rot_bzs = MBezierUtils.join_value_2_bezier(fill_bfs[-1].fno, next_bf.name, list(rot_values), offset=offset, diff_limit=0.1) if is_rot else True + joined_x_bzs = MBezierUtils.join_value_2_bezier(fill_bfs[-1].fno, next_bf.name, list(x_values), offset=offset, diff_limit=0.01) if is_mov else True + joined_y_bzs = MBezierUtils.join_value_2_bezier(fill_bfs[-1].fno, next_bf.name, list(y_values), offset=offset, diff_limit=0.01) if is_mov else True + joined_z_bzs = MBezierUtils.join_value_2_bezier(fill_bfs[-1].fno, next_bf.name, list(z_values), offset=offset, diff_limit=0.01) if is_mov else True + + if joined_rot_bzs and joined_x_bzs and joined_y_bzs and joined_z_bzs: + # 結合できた場合、補間曲線をnextに設定 + if is_rot: + self.reset_interpolation_parts(prev_bf.name, next_bf, joined_rot_bzs, MBezierUtils.R_x1_idxs, MBezierUtils.R_y1_idxs, MBezierUtils.R_x2_idxs, MBezierUtils.R_y2_idxs) + + if is_mov: + self.reset_interpolation_parts(prev_bf.name, next_bf, joined_x_bzs, MBezierUtils.MX_x1_idxs, MBezierUtils.MX_y1_idxs, MBezierUtils.MX_x2_idxs, MBezierUtils.MX_y2_idxs) + self.reset_interpolation_parts(prev_bf.name, next_bf, joined_y_bzs, MBezierUtils.MY_x1_idxs, MBezierUtils.MY_y1_idxs, MBezierUtils.MY_x2_idxs, MBezierUtils.MY_y2_idxs) + self.reset_interpolation_parts(prev_bf.name, next_bf, joined_z_bzs, MBezierUtils.MZ_x1_idxs, MBezierUtils.MZ_y1_idxs, MBezierUtils.MZ_x2_idxs, MBezierUtils.MZ_y2_idxs) + + return True + + # 結合できなかった場合、False + return False + # 指定ボーンの不要キーを削除する # 変曲点を求める @@ -566,13 +689,14 @@ cdef class VmdMotion: else: # 範囲指定がある場合はその範囲内だけ fnos = self.get_bone_fnos(bone_name, start_fno=r_start_fno, end_fno=r_end_fno) - logger.debug("remove_unnecessary_bf prev: %s, %s", bone_name, len(fnos)) + logger.debug("remove_unnecessary_bf fnos: %s, %s", bone_name, fnos) if len(fnos) <= 1: return - cdef int fno = 1 - cdef int start_fno = 0 + cdef int fno = fnos[0] + 1 + cdef int start_fno = fnos[0] + cdef int last_fno = fnos[0] cdef list rot_values = [0] cdef list mx_values = [0] cdef list my_values = [0] @@ -597,12 +721,10 @@ cdef class VmdMotion: cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mz_f_prime cdef np.ndarray[DTYPE_FLOAT_t, ndim=1] mz_sign cdef np.ndarray[DTYPE_INT_t, ndim=1] mz_np_indices = np.array([], dtype=np.int) - cdef np.ndarray[DTYPE_INT_t, ndim=1] indices cdef int inflection cdef int inflection_fno cdef list active_fnos cdef bint is_inflection = False - cdef int last_fno = 0 # cdef list joined_rot_bzs, joined_mx_bzs, joined_my_bzs, joined_mz_bzs, rot_inflection, mx_inflection, my_inflection, mz_inflection while fno <= fnos[-1]: @@ -615,6 +737,8 @@ cdef class VmdMotion: prev_bf = self.c_calc_bf(bone_name, start_fno, is_key=False, is_read=False, is_reset_interpolation=False) logger.debug("*%s: f: %s, prev_bf(%s):rot:%s", bone_name, fno, prev_bf.fno, prev_bf.rotation.toEulerAngles4MMD().to_log()) + next_bf = None + # 変化量を保持 rot_values.append(bf.rotation.calcTheata(prev_bf.rotation)) mx_values.append(bf.position.x() - prev_bf.position.x()) @@ -625,216 +749,158 @@ cdef class VmdMotion: # 必要な個数が溜まるまでスルー fno += 1 continue - - # 単調増加としてキーを結合してみる - (joined_rot_bzs, rot_inflection) = MBezierUtils.join_value_2_bezier(fno, bone_name, rot_values, \ - offset=offset, diff_limit=rot_diff_limit) if is_rot else (True, []) - (joined_mx_bzs, mx_inflection) = MBezierUtils.join_value_2_bezier(fno, bone_name, mx_values, \ - offset=offset, diff_limit=mov_diff_limit) if is_mov else (True, []) - (joined_my_bzs, my_inflection) = MBezierUtils.join_value_2_bezier(fno, bone_name, my_values, \ - offset=offset, diff_limit=mov_diff_limit) if is_mov else (True, []) - (joined_mz_bzs, mz_inflection) = MBezierUtils.join_value_2_bezier(fno, bone_name, mz_values, \ - offset=offset, diff_limit=mov_diff_limit) if is_mov else (True, []) - - if joined_rot_bzs and joined_mx_bzs and joined_my_bzs and joined_mz_bzs: - next_bf = self.c_calc_bf(bone_name, fno, is_key=False, is_read=False, is_reset_interpolation=False) - # 結合できた場合、補間曲線をnextに設定 - if is_rot and len(joined_rot_bzs) > 0: - logger.debug("☆%s: f: %s, キー:回転補間曲線成功: 1: %s, 2: %s", bone_name, fno, joined_rot_bzs[1].to_log(), joined_rot_bzs[2].to_log()) - self.reset_interpolation_parts(bone_name, next_bf, joined_rot_bzs, MBezierUtils.R_x1_idxs, MBezierUtils.R_y1_idxs, MBezierUtils.R_x2_idxs, MBezierUtils.R_y2_idxs) - - if is_mov and len(joined_mx_bzs) > 0 and len(joined_my_bzs) > 0 and len(joined_mz_bzs) > 0: - logger.debug("☆%s: f: %s, キー:移動X補間曲線成功: 1: %s, 2: %s", bone_name, fno, joined_mx_bzs[1].to_log(), joined_mx_bzs[2].to_log()) - logger.debug("☆%s: f: %s, キー:移動Y補間曲線成功: 1: %s, 2: %s", bone_name, fno, joined_my_bzs[1].to_log(), joined_my_bzs[2].to_log()) - logger.debug("☆%s: f: %s, キー:移動Z補間曲線成功: 1: %s, 2: %s", bone_name, fno, joined_mz_bzs[1].to_log(), joined_mz_bzs[2].to_log()) - self.reset_interpolation_parts(bone_name, next_bf, joined_mx_bzs, MBezierUtils.MX_x1_idxs, MBezierUtils.MX_y1_idxs, MBezierUtils.MX_x2_idxs, MBezierUtils.MX_y2_idxs) - self.reset_interpolation_parts(bone_name, next_bf, joined_my_bzs, MBezierUtils.MY_x1_idxs, MBezierUtils.MY_y1_idxs, MBezierUtils.MY_x2_idxs, MBezierUtils.MY_y2_idxs) - self.reset_interpolation_parts(bone_name, next_bf, joined_mz_bzs, MBezierUtils.MZ_x1_idxs, MBezierUtils.MZ_y1_idxs, MBezierUtils.MZ_x2_idxs, MBezierUtils.MZ_y2_idxs) - - if bone_name in self.bones and fno in self.bones[bone_name]: - self.bones[bone_name][fno].key = True - self.bones[bone_name][fno].interpolation = next_bf.interpolation - logger.debug("**◇%s: f: %s, next_bf(%s):rot:%s", bone_name, fno, next_bf.fno, next_bf.rotation.toEulerAngles4MMD().to_log()) + + if bf.key: + # 単調増加としてキーを結合してみる + (joined_rot_bzs, rot_inflection) = MBezierUtils.join_value_2_bezier(fno, bone_name, rot_values, \ + offset=offset, diff_limit=rot_diff_limit) if is_rot else (True, []) + (joined_mx_bzs, mx_inflection) = MBezierUtils.join_value_2_bezier(fno, bone_name, mx_values, \ + offset=offset, diff_limit=mov_diff_limit) if is_mov else (True, []) + (joined_my_bzs, my_inflection) = MBezierUtils.join_value_2_bezier(fno, bone_name, my_values, \ + offset=offset, diff_limit=mov_diff_limit) if is_mov else (True, []) + (joined_mz_bzs, mz_inflection) = MBezierUtils.join_value_2_bezier(fno, bone_name, mz_values, \ + offset=offset, diff_limit=mov_diff_limit) if is_mov else (True, []) + + if joined_rot_bzs and joined_mx_bzs and joined_my_bzs and joined_mz_bzs: + next_bf = self.c_calc_bf(bone_name, fno, is_key=False, is_read=False, is_reset_interpolation=False) + + # 結合できた場合、補間曲線をnextに設定 + if is_rot and len(joined_rot_bzs) > 0: + logger.debug("☆%s: f: %s(%s), キー:回転補間曲線成功: 1: %s, 2: %s", bone_name, fno, start_fno, joined_rot_bzs[1].to_log(), joined_rot_bzs[2].to_log()) + self.reset_interpolation_parts(bone_name, next_bf, joined_rot_bzs, MBezierUtils.R_x1_idxs, MBezierUtils.R_y1_idxs, MBezierUtils.R_x2_idxs, MBezierUtils.R_y2_idxs) + + if is_mov and len(joined_mx_bzs) > 0 and len(joined_my_bzs) > 0 and len(joined_mz_bzs) > 0: + logger.debug("☆%s: f: %s(%s), キー:移動X補間曲線成功: 1: %s, 2: %s", bone_name, fno, start_fno, joined_mx_bzs[1].to_log(), joined_mx_bzs[2].to_log()) + logger.debug("☆%s: f: %s(%s), キー:移動Y補間曲線成功: 1: %s, 2: %s", bone_name, fno, start_fno, joined_my_bzs[1].to_log(), joined_my_bzs[2].to_log()) + logger.debug("☆%s: f: %s(%s), キー:移動Z補間曲線成功: 1: %s, 2: %s", bone_name, fno, start_fno, joined_mz_bzs[1].to_log(), joined_mz_bzs[2].to_log()) + self.reset_interpolation_parts(bone_name, next_bf, joined_mx_bzs, MBezierUtils.MX_x1_idxs, MBezierUtils.MX_y1_idxs, MBezierUtils.MX_x2_idxs, MBezierUtils.MX_y2_idxs) + self.reset_interpolation_parts(bone_name, next_bf, joined_my_bzs, MBezierUtils.MY_x1_idxs, MBezierUtils.MY_y1_idxs, MBezierUtils.MY_x2_idxs, MBezierUtils.MY_y2_idxs) + self.reset_interpolation_parts(bone_name, next_bf, joined_mz_bzs, MBezierUtils.MZ_x1_idxs, MBezierUtils.MZ_y1_idxs, MBezierUtils.MZ_x2_idxs, MBezierUtils.MZ_y2_idxs) + + last_fno = fno + + # 次も結合テストするため、分離はしない + is_inflection = False else: - self.c_regist_bf(next_bf, bone_name, fno, copy_interpolation=True) - logger.debug("**☆%s: f: %s, next_bf(%s):rot:%s", bone_name, fno, next_bf.fno, next_bf.rotation.toEulerAngles4MMD().to_log()) - - for f in range(start_fno + 1, fno): + # 結合できなかった場合、変曲点チェックに移る + logger.debug("★%s: f: %s(%s), キー:補間曲線失敗: rot_inflection: %s, mx_inflection: %s, my_inflection: %s, mz_inflection: %s", \ + bone_name, fno, start_fno, rot_inflection, mx_inflection, my_inflection, mz_inflection) + + # 前回の分離点で分離する + is_inflection = True + + # # 変曲点があった場合、そこで区切る + # indices = np.array([]) + # indices = np.append(indices, rot_inflection) + # indices = np.append(indices, mx_inflection) + # indices = np.append(indices, my_inflection) + # indices = np.append(indices, mz_inflection) + + # if indices.size > 0: + # # 昇順に並べ替える + # indices.sort() + # logger.debug("indices: %s", indices) + # # 変曲点で区切る + # inflection = (indices[0]) + + # if inflection <= 0: + # fno += 1 + # continue + + # inflection_fno = start_fno + inflection + # logger.debug("☆%s: 変曲点: %s, start_f: %s, f: %s, indices: %s", bone_name, inflection_fno, start_fno, fno, indices) + + # # 結合したベジェ曲線 + # (joined_rot_bzs, rot_inflection) = MBezierUtils.join_value_2_bezier(inflection_fno, bone_name, rot_values[:(inflection + 1)], \ + # offset=offset, diff_limit=rot_diff_limit) if is_rot else (True, []) + # (joined_mx_bzs, mx_inflection) = MBezierUtils.join_value_2_bezier(inflection_fno, bone_name, mx_values[:(inflection + 1)], \ + # offset=offset, diff_limit=mov_diff_limit) if is_mov else (True, []) + # (joined_my_bzs, my_inflection) = MBezierUtils.join_value_2_bezier(inflection_fno, bone_name, my_values[:(inflection + 1)], \ + # offset=offset, diff_limit=mov_diff_limit) if is_mov else (True, []) + # (joined_mz_bzs, mz_inflection) = MBezierUtils.join_value_2_bezier(inflection_fno, bone_name, mz_values[:(inflection + 1)], \ + # offset=offset, diff_limit=mov_diff_limit) if is_mov else (True, []) + + # if joined_rot_bzs and joined_mx_bzs and joined_my_bzs and joined_mz_bzs: + # next_bf = self.c_calc_bf(bone_name, inflection_fno, is_key=False, is_read=False, is_reset_interpolation=False) + + # # 結合できた場合、補間曲線をnextに設定 + # if is_rot: + # logger.debug("☆%s: f: %s(%s), 変曲点:回転補間曲線成功: 1: %s, 2: %s", bone_name, inflection_fno, start_fno, joined_rot_bzs[1].to_log(), joined_rot_bzs[2].to_log()) + # self.reset_interpolation_parts(bone_name, next_bf, joined_rot_bzs, MBezierUtils.R_x1_idxs, MBezierUtils.R_y1_idxs, MBezierUtils.R_x2_idxs, MBezierUtils.R_y2_idxs) + + # if is_mov: + # logger.debug("☆%s: f: %s(%s), 変曲点:移動X補間曲線成功: 1: %s, 2: %s", bone_name, inflection_fno, start_fno, joined_mx_bzs[1].to_log(), joined_mx_bzs[2].to_log()) + # logger.debug("☆%s: f: %s(%s), 変曲点:移動Y補間曲線成功: 1: %s, 2: %s", bone_name, inflection_fno, start_fno, joined_my_bzs[1].to_log(), joined_my_bzs[2].to_log()) + # logger.debug("☆%s: f: %s(%s), 変曲点:移動Z補間曲線成功: 1: %s, 2: %s", bone_name, inflection_fno, start_fno, joined_mz_bzs[1].to_log(), joined_mz_bzs[2].to_log()) + # self.reset_interpolation_parts(bone_name, next_bf, joined_mx_bzs, MBezierUtils.MX_x1_idxs, MBezierUtils.MX_y1_idxs, MBezierUtils.MX_x2_idxs, MBezierUtils.MX_y2_idxs) + # self.reset_interpolation_parts(bone_name, next_bf, joined_my_bzs, MBezierUtils.MY_x1_idxs, MBezierUtils.MY_y1_idxs, MBezierUtils.MY_x2_idxs, MBezierUtils.MY_y2_idxs) + # self.reset_interpolation_parts(bone_name, next_bf, joined_mz_bzs, MBezierUtils.MZ_x1_idxs, MBezierUtils.MZ_y1_idxs, MBezierUtils.MZ_x2_idxs, MBezierUtils.MZ_y2_idxs) + + # # 最終結合キーを置き換える + # last_fno = inflection_fno + # else: + # # 結合できなかった場合、前回最終結合キーで分離する + # logger.debug("★%s: f: %s(%s), 変曲点:補間曲線失敗: rot_inflection: %s, mx_inflection: %s, my_inflection: %s, mz_inflection: %s", \ + # bone_name, inflection_fno, start_fno, rot_inflection, mx_inflection, my_inflection, mz_inflection) + # else: + # # 変曲点がない場合(曲線が補間曲線に合わない場合)、前回最終結合キーで分離する + # logger.debug("変曲点が見つからなかった場合") + + # 変曲点で登録 + if next_bf: + if bone_name in self.bones and last_fno in self.bones[bone_name]: + self.bones[bone_name][last_fno].key = True + self.bones[bone_name][last_fno].interpolation = next_bf.interpolation + logger.debug("◇登録 %s: f: %s, next_bf(%s) rot:%s", bone_name, last_fno, next_bf.fno, next_bf.rotation.toEulerAngles4MMD().to_log()) + else: + self.c_regist_bf(next_bf, bone_name, last_fno, copy_interpolation=True) + logger.debug("☆登録 %s: f: %s, next_bf(%s) rot:%s", bone_name, last_fno, next_bf.fno, next_bf.rotation.toEulerAngles4MMD().to_log()) + + for f in range(start_fno + 1, last_fno): # 結合できた場合、区間内を削除 if f in self.bones[bone_name]: - logger.debug("☆%s: f: %s, キー:キーフレ削除: df: %s", bone_name, fno, f) + logger.debug("☆%s: f: %s, キーフレ削除: df: %s", bone_name, last_fno, f) self.bones[bone_name][f].key = False - - logger.debug("**☆有効キー %s: %s", bone_name, self.get_bone_fnos(bone_name, is_key=True)) - last_fno = fno - fno += 1 + if is_inflection: + logger.debug("%s, 開始キーフレ変更前: start_fno: %s, fno: %s, last_fno: %s", bone_name, start_fno, fno, last_fno) - # 変曲点処理は不要 - continue - else: - # 結合できなかった場合、変曲点チェックに移る - logger.debug("★%s: f: %s, キー:補間曲線失敗: rot_inflection: %s, mx_inflection: %s, my_inflection: %s, mz_inflection: %s", \ - bone_name, fno, rot_inflection, mx_inflection, my_inflection, mz_inflection) - - # 近似差分から変曲点を求める - # https://teratail.com/questions/162391 - rot_indices = [] - mx_indices = [] - my_indices = [] - mz_indices = [] - rot_np_values = np.ndarray([], dtype=np.float64) - mx_np_values = np.ndarray([], dtype=np.float64) - my_np_values = np.ndarray([], dtype=np.float64) - mz_np_values = np.ndarray([], dtype=np.float64) - if is_rot and len(rot_values) > 1: - rot_f_prime = np.gradient(rot_values) - rot_sign = np.concatenate([[0], np.diff(np.sign(rot_f_prime))]) - rot_np_indices = np.where(np.abs(rot_sign) > 1)[0].astype(np.int) - logger.debug("%s: f: %s, rot_values: %s", bone_name, fno, rot_values) - logger.debug("%s: f: %s, f_prime: %s", bone_name, fno, rot_f_prime) - logger.debug("%s: f: %s, sign: %s", bone_name, fno, rot_sign) - logger.debug("%s: f: %s, rot_np_indices: %s", bone_name, fno, rot_np_indices) - - if is_mov and len(mx_values) > 1: - mx_f_prime = np.gradient(mx_values) - mx_sign = np.concatenate([[0], [0], np.diff(np.sign(np.diff(mx_f_prime)))]) - mx_np_indices = np.where(np.abs(mx_sign) > 1)[0].astype(np.int) - logger.debug("%s: f: %s, mx_values: %s", bone_name, fno, mx_values) - logger.debug("%s: f: %s, f_prime: %s", bone_name, fno, mx_f_prime) - logger.debug("%s: f: %s, sign: %s", bone_name, fno, mx_sign) - logger.debug("%s: f: %s, mx_np_indices: %s", bone_name, fno, mx_np_indices) - - my_f_prime = np.gradient(my_values) - my_sign = np.concatenate([[0], [0], np.diff(np.sign(np.diff(my_f_prime)))]) - my_np_indices = np.where(np.abs(my_sign) > 1)[0].astype(np.int) - logger.debug("%s: f: %s, my_values: %s", bone_name, fno, my_values) - logger.debug("%s: f: %s, f_prime: %s", bone_name, fno, my_f_prime) - logger.debug("%s: f: %s, sign: %s", bone_name, fno, my_sign) - logger.debug("%s: f: %s, my_np_indices: %s", bone_name, fno, my_np_indices) - - mz_f_prime = np.gradient(mz_values) - mz_sign = np.concatenate([[0], [0], np.diff(np.sign(np.diff(mz_f_prime)))]) - mz_np_indices = np.where(np.abs(mz_sign) > 1)[0].astype(np.int) - logger.debug("%s: f: %s, mz_values: %s", bone_name, fno, mz_values) - logger.debug("%s: f: %s, f_prime: %s", bone_name, fno, mz_f_prime) - logger.debug("%s: f: %s, sign: %s", bone_name, fno, mz_sign) - logger.debug("%s: f: %s, mz_np_indices: %s", bone_name, fno, mz_np_indices) - - if rot_np_indices.size + mx_np_indices.size + my_np_indices.size + mz_np_indices.size > 0: - logger.debug("変曲点がある場合") - - # 不要なキーを連結する - # 変曲点があった場合、そこで区切る - indices = np.array([], dtype=np.int) - indices = np.append(indices, rot_np_indices) - indices = np.append(indices, mx_np_indices) - indices = np.append(indices, my_np_indices) - indices = np.append(indices, mz_np_indices) - - if indices.size > 0: - # 昇順に並べ替える - indices.sort() - logger.debug("indices: %s", indices) - # 変曲点で区切る - inflection = (indices[0]) - - if inflection <= 0: - fno += 1 - continue + # 開始キーフレは、変曲点までずらす + fno = last_fno if start_fno != last_fno else last_fno + 1 + start_fno = fno + last_fno = fno - inflection_fno = start_fno + inflection - logger.debug("☆%s: 変曲点: %s, start_f: %s, f: %s, indices: %s", bone_name, inflection_fno, start_fno, fno, indices) - - next_bf = self.c_calc_bf(bone_name, inflection_fno, is_key=False, is_read=False, is_reset_interpolation=False) - - if inflection > 0: - # 結合したベジェ曲線 - (joined_rot_bzs, rot_inflection) = MBezierUtils.join_value_2_bezier(inflection_fno, bone_name, rot_values[:(inflection + 1)], \ - offset=offset, diff_limit=rot_diff_limit) if is_rot else (True, []) - (joined_mx_bzs, mx_inflection) = MBezierUtils.join_value_2_bezier(inflection_fno, bone_name, mx_values[:(inflection + 1)], \ - offset=offset, diff_limit=mov_diff_limit) if is_mov else (True, []) - (joined_my_bzs, my_inflection) = MBezierUtils.join_value_2_bezier(inflection_fno, bone_name, my_values[:(inflection + 1)], \ - offset=offset, diff_limit=mov_diff_limit) if is_mov else (True, []) - (joined_mz_bzs, mz_inflection) = MBezierUtils.join_value_2_bezier(inflection_fno, bone_name, mz_values[:(inflection + 1)], \ - offset=offset, diff_limit=mov_diff_limit) if is_mov else (True, []) - - if joined_rot_bzs and joined_mx_bzs and joined_my_bzs and joined_mz_bzs: - # 結合できた場合、補間曲線をnextに設定 - if is_rot: - logger.debug("☆%s: f: %s, 変曲点:回転補間曲線成功: 1: %s, 2: %s", bone_name, inflection_fno, joined_rot_bzs[1].to_log(), joined_rot_bzs[2].to_log()) - self.reset_interpolation_parts(bone_name, next_bf, joined_rot_bzs, MBezierUtils.R_x1_idxs, MBezierUtils.R_y1_idxs, MBezierUtils.R_x2_idxs, MBezierUtils.R_y2_idxs) - - if is_mov: - logger.debug("☆%s: f: %s, 変曲点:移動X補間曲線成功: 1: %s, 2: %s", bone_name, inflection_fno, joined_mx_bzs[1].to_log(), joined_mx_bzs[2].to_log()) - logger.debug("☆%s: f: %s, 変曲点:移動Y補間曲線成功: 1: %s, 2: %s", bone_name, inflection_fno, joined_my_bzs[1].to_log(), joined_my_bzs[2].to_log()) - logger.debug("☆%s: f: %s, 変曲点:移動Z補間曲線成功: 1: %s, 2: %s", bone_name, inflection_fno, joined_mz_bzs[1].to_log(), joined_mz_bzs[2].to_log()) - self.reset_interpolation_parts(bone_name, next_bf, joined_mx_bzs, MBezierUtils.MX_x1_idxs, MBezierUtils.MX_y1_idxs, MBezierUtils.MX_x2_idxs, MBezierUtils.MX_y2_idxs) - self.reset_interpolation_parts(bone_name, next_bf, joined_my_bzs, MBezierUtils.MY_x1_idxs, MBezierUtils.MY_y1_idxs, MBezierUtils.MY_x2_idxs, MBezierUtils.MY_y2_idxs) - self.reset_interpolation_parts(bone_name, next_bf, joined_mz_bzs, MBezierUtils.MZ_x1_idxs, MBezierUtils.MZ_y1_idxs, MBezierUtils.MZ_x2_idxs, MBezierUtils.MZ_y2_idxs) - - # 最終結合キーを置き換える - last_fno = inflection_fno - is_inflection = True - else: - # 結合できなかった場合、スルー - logger.debug("★%s: f: %s, 変曲点:補間曲線失敗: rot_inflection: %s, mx_inflection: %s, my_inflection: %s, mz_inflection: %s", \ - bone_name, inflection_fno, rot_inflection, mx_inflection, my_inflection, mz_inflection) - # 前回結合できたキーフレで置き換える - is_inflection = True - else: - # キーの結合に失敗して、かつ変曲点がない場合、前回結合できたキーフレの次に置き換える - logger.debug("キーの結合に失敗して、かつ変曲点がない場合") - is_inflection = True + # 配列初期化 + rot_values = [0] + mx_values = [0] + my_values = [0] + mz_values = [0] + rot_np_values = np.ndarray([], dtype=np.float64) + mx_np_values = np.ndarray([], dtype=np.float64) + my_np_values = np.ndarray([], dtype=np.float64) + mz_np_values = np.ndarray([], dtype=np.float64) - if fno // 100 > prev_sep_fno and fnos[-1] > 0: + logger.debug("%s, 開始キーフレ変更後: start_fno: %s, fno: %s, last_fno: %s", bone_name, start_fno, fno, last_fno) + + if fno // 300 > prev_sep_fno and fnos[-1] > 0 and is_show_log: if data_set_no == 0: logger.info("-- %sフレーム目:終了(%s%)【不要キー削除 - %s】", fno, round((fno / fnos[-1]) * 100, 3), bone_name) else: logger.info("-- %sフレーム目:終了(%s%)【No.%s - 不要キー削除 - %s】", fno, round((fno / fnos[-1]) * 100, 3), data_set_no, bone_name) - prev_sep_fno = fno // 100 - - # 変曲点で登録 - if next_bf: - if bone_name in self.bones and last_fno in self.bones[bone_name]: - self.bones[bone_name][last_fno].key = True - self.bones[bone_name][last_fno].interpolation = next_bf.interpolation - logger.debug("◇登録 %s: f: %s, next_bf(%s):rot:%s", bone_name, last_fno, next_bf.fno, next_bf.rotation.toEulerAngles4MMD().to_log()) - else: - self.c_regist_bf(next_bf, bone_name, last_fno, copy_interpolation=True) - logger.debug("☆登録 %s: f: %s, next_bf(%s):rot:%s", bone_name, last_fno, next_bf.fno, next_bf.rotation.toEulerAngles4MMD().to_log()) - - for f in range(start_fno + 1, last_fno): - # 結合できた場合、区間内を削除 - if f in self.bones[bone_name]: - logger.debug("☆%s: f: %s, 変曲点:キーフレ削除: df: %s", bone_name, last_fno, f) - self.bones[bone_name][f].key = False - - if is_inflection: - logger.debug("%s: f: %s, 開始キーフレ変更: start_fno: %s, fno: %s", bone_name, last_fno, start_fno, fno) - - # 開始キーフレは、変曲点までずらす - start_fno = last_fno + (0 if last_fno > start_fno else 1) - # 最終結合キーフレを変曲点までずらす - last_fno = start_fno - # fnoを変曲点まで戻す - fno = start_fno - - # 配列初期化 - rot_values = [0] - mx_values = [0] - my_values = [0] - mz_values = [0] - rot_np_values = np.ndarray([], dtype=np.float64) - mx_np_values = np.ndarray([], dtype=np.float64) - my_np_values = np.ndarray([], dtype=np.float64) - mz_np_values = np.ndarray([], dtype=np.float64) + prev_sep_fno = fno // 300 # 必ず進める fno += 1 - logger.debug("**☆有効キー(%s-%s) %s: %s", bone_name, start_fno, fno, self.get_bone_fnos(bone_name, is_key=True)) + if r_start_fno < 0 and r_end_fno < 0: + # 範囲指定がない場合、全範囲 + active_fnos = self.get_bone_fnos(bone_name, is_key=True) + else: + # 範囲指定がある場合はその範囲内だけ + active_fnos = self.get_bone_fnos(bone_name, start_fno=r_start_fno, end_fno=r_end_fno, is_key=True) + + logger.debug("**☆有効キー(%s-%s) %s: %s", bone_name, start_fno, fno, active_fnos) logger.debug("len: %s, last: %s", len(rot_values), fnos[-1]) @@ -847,14 +913,19 @@ cdef class VmdMotion: if r_start_fno < 0 and r_end_fno < 0: # 範囲指定がない場合、全範囲 active_fnos = self.get_bone_fnos(bone_name, is_key=True) + + for fno in range(fnos[-1]): + if fno in self.bones[bone_name] and fno not in active_fnos: + # 最後に物理削除 + del self.bones[bone_name][fno] else: # 範囲指定がある場合はその範囲内だけ active_fnos = self.get_bone_fnos(bone_name, start_fno=r_start_fno, end_fno=r_end_fno, is_key=True) - for fno in range(fnos[-1]): - if fno in self.bones[bone_name] and fno not in active_fnos: - # 最後に物理削除 - del self.bones[bone_name][fno] + for fno in range(r_start_fno, r_end_fno): + if fno in self.bones[bone_name] and fno not in active_fnos: + # 最後に物理削除 + del self.bones[bone_name][fno] logger.debug("remove_unnecessary_bf after: %s, %s, all: %s", bone_name, active_fnos, len(fnos)) diff --git a/src/module/MMath.pyx b/src/module/MMath.pyx index fa0aac2..7ce16aa 100644 --- a/src/module/MMath.pyx +++ b/src/module/MMath.pyx @@ -1067,10 +1067,12 @@ cdef class MQuaternion: # 自分ともうひとつの値vとのtheta(変位量)を返す cpdef double calcTheata(self, MQuaternion v): - cdef double dot = MQuaternion.dotProduct(self.normalized(), v.normalized()) - cdef double theta = acos(min(1, max(-1, dot))) - return theta - + return (1 - MQuaternion.dotProduct(self.normalized(), v.normalized())) + # cdef double dot = MQuaternion.dotProduct(self.normalized(), v.normalized()) + # cdef double angle = acos(min(1, max(-1, dot))) + # cdef double sinOfAngle = sin(angle) + # return sinOfAngle + @classmethod def dotProduct(cls, v1, v2): return dotProduct_MQuaternion(v1, v2) diff --git a/src/module/MOptions.pxd b/src/module/MOptions.pxd index 95a24ba..06537c8 100644 --- a/src/module/MOptions.pxd +++ b/src/module/MOptions.pxd @@ -31,6 +31,7 @@ cdef class MOptions(): cdef public MArmProcessOptions arm_options cdef public VmdMotion camera_motion cdef public str camera_output_vmd_path + cdef public float camera_length cdef public object monitor cdef public bint is_file cdef public str outout_datetime diff --git a/src/module/MOptions.pyx b/src/module/MOptions.pyx index 0001994..4a8b223 100644 --- a/src/module/MOptions.pyx +++ b/src/module/MOptions.pyx @@ -21,7 +21,7 @@ logger = MLogger(__name__) cdef class MOptions(): def __init__(self, version_name, logging_level, max_workers, data_set_list, arm_options, \ - camera_motion, camera_output_vmd_path, monitor, is_file, outout_datetime, total_process, now_process, total_process_ctrl, now_process_ctrl, tree_process_dict): + camera_motion, camera_output_vmd_path, camera_length, monitor, is_file, outout_datetime, total_process, now_process, total_process_ctrl, now_process_ctrl, tree_process_dict): self.version_name = version_name self.logging_level = logging_level self.max_workers = max_workers @@ -29,6 +29,7 @@ cdef class MOptions(): self.arm_options = arm_options self.camera_motion = camera_motion self.camera_output_vmd_path = camera_output_vmd_path + self.camera_length = camera_length self.monitor = monitor self.is_file = is_file self.outout_datetime = outout_datetime diff --git a/src/service/ConvertSmoothService.py b/src/service/ConvertSmoothService.py index 06287c3..d088b07 100644 --- a/src/service/ConvertSmoothService.py +++ b/src/service/ConvertSmoothService.py @@ -115,11 +115,11 @@ def remove_filterd_bf(self, bone_name: str): for n in range(1, self.options.loop_cnt): # 処理回数が3回以上の場合、フィルタをかける - if self.options.loop_cnt > 2: + if self.options.loop_cnt > 2 and n > 2: logger.info("【フィルタリング%s回目】%s 開始", n - 1, bone_name) self.options.motion.smooth_filter_bf(0, bone_name, self.options.model.bones[bone_name].getRotatable(), \ self.options.model.bones[bone_name].getTranslatable(), \ - config={"freq": 30, "mincutoff": 0.1, "beta": 0.1, "dcutoff": 1}) + config={"freq": 30, "mincutoff": 1, "beta": 1, "dcutoff": 1}) logger.info("【フィルタリング%s回目】%s 終了", n - 1, bone_name) logger.info("【不要キー削除%s回目】%s 開始", n, bone_name) @@ -142,8 +142,8 @@ def prepare_linear(self, bone_name: str): logger.info("【スムージング1回目】%s 開始", bone_name) - # 各ボーンのbfを全打ち - self.options.motion.regist_full_bf(1, [bone_name], offset=1) + # 各ボーンのbfを全打ち(スムージングが複数回数の場合、キー自体は無効のまま) + self.options.motion.regist_full_bf(1, [bone_name], offset=1, is_key=(self.options.loop_cnt <= 1)) logger.info("【スムージング1回目】%s 終了", bone_name) diff --git a/src/service/SizingService.py b/src/service/SizingService.py index 8c874ea..fa6b445 100644 --- a/src/service/SizingService.py +++ b/src/service/SizingService.py @@ -75,8 +75,8 @@ def execute(self): arm_check_skip=self.options.arm_options.arm_check_skip_flg) # noqa if self.options.camera_motion: - service_data_txt = "{service_data_txt}カメラ: {camera}\n".format(service_data_txt=service_data_txt, - camera=os.path.basename(self.options.camera_motion.path)) # noqa + service_data_txt = "{service_data_txt}カメラ: {camera}({camera_length})\n".format(service_data_txt=service_data_txt, + camera=os.path.basename(self.options.camera_motion.path), camera_length=self.options.camera_length) # noqa service_data_txt = "{service_data_txt}------------------------".format(service_data_txt=service_data_txt) # noqa diff --git a/src/service/parts/CameraService.py b/src/service/parts/CameraService.py index 563a1f3..e856708 100644 --- a/src/service/parts/CameraService.py +++ b/src/service/parts/CameraService.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # import numpy as np +import math from mmd.PmxData import PmxModel, Bone # noqa from mmd.VmdData import VmdMotion, VmdBoneFrame, VmdCameraFrame, VmdInfoIk, VmdLightFrame, VmdMorphFrame, VmdShadowFrame, VmdShowIkFrame # noqa @@ -63,7 +64,8 @@ def execute(self): # カメラサイジング実行 self.execute_rep_camera(fno, cf, org_inner_global_poses, org_inner_square_poses, rep_inner_global_poses, ratio, nearest_data_set_idx, nearest_bone_name, \ - left_data_set_idx, left_bone_name, right_data_set_idx, right_bone_name, top_data_set_idx, top_bone_name, bottom_data_set_idx, bottom_bone_name) + left_data_set_idx, left_bone_name, right_data_set_idx, right_bone_name, top_data_set_idx, top_bone_name, bottom_data_set_idx, bottom_bone_name, \ + self.options.camera_length) prev_fno = fno @@ -87,7 +89,7 @@ def execute(self): # 変換先モデル用カメラ作成 def execute_rep_camera(self, fno: int, cf: VmdCameraFrame, org_inner_global_poses: dict, org_inner_square_poses: dict, rep_inner_global_poses: dict, \ ratio: float, nearest_data_set_idx: int, nearest_bone_name: str, left_data_set_idx: int, left_bone_name: str, right_data_set_idx: int, right_bone_name: str, \ - top_data_set_idx: int, top_bone_name: str, bottom_data_set_idx: int, bottom_bone_name: str): + top_data_set_idx: int, top_bone_name: str, bottom_data_set_idx: int, bottom_bone_name: str, camera_length: float): # ---------------- # 画面内に映ってるボーンINDEXの中央値を仮の中央座標とする @@ -131,7 +133,9 @@ def execute_rep_camera(self, fno: int, cf: VmdCameraFrame, org_inner_global_pose cf.position = camera_length_origin # カメラの距離を再設定 - cf.length = cf.length * ratio + # 可動範囲内に収める(2020/10/22) + limit_ratio = min(camera_length, max(1 / camera_length, ratio)) if camera_length < 5 else ratio + cf.length = cf.length * limit_ratio offset_length = 0 offset_angle = 0 @@ -179,7 +183,7 @@ def execute_rep_camera(self, fno: int, cf: VmdCameraFrame, org_inner_global_pose cnt += 1 logger.info("%sフレーム目 縮尺比率: %s, 注視点: %s-%s, 上辺: %s-%s, 下辺: %s-%s, 座標: %s, 距離: %s, 視野角: %s", \ - cf.fno, round(ratio, 5), (nearest_data_set_idx + 1), nearest_bone_name.replace("実体", ""), \ + cf.fno, round(limit_ratio, 5), (nearest_data_set_idx + 1), nearest_bone_name.replace("実体", ""), \ (top_data_set_idx + 1), top_bone_name.replace("実体", ""), (bottom_data_set_idx + 1), bottom_bone_name.replace("実体", ""), \ org_nearest_relative_vec.to_log(), round(offset_length, 5), offset_angle) diff --git a/src/utils/MBezierUtils.pxd b/src/utils/MBezierUtils.pxd index d863658..3f1ea64 100644 --- a/src/utils/MBezierUtils.pxd +++ b/src/utils/MBezierUtils.pxd @@ -7,7 +7,7 @@ from module.MMath cimport MRect, MVector2D, MVector3D, MVector4D, MQuaternion, M cdef double calc_catmull_rom_one_point(double x, double v0, double v1, double v2, double v3) except? -1 -cdef np.ndarray calc_value_from_catmullrom(str bone_name, list fnos, list values) +cpdef np.ndarray calc_value_from_catmullrom(str bone_name, list fnos, list values) cdef bint fit_bezier_mmd(list bzs) diff --git a/src/utils/MBezierUtils.pyx b/src/utils/MBezierUtils.pyx index 3c30e05..ea5d55f 100644 --- a/src/utils/MBezierUtils.pyx +++ b/src/utils/MBezierUtils.pyx @@ -6,6 +6,7 @@ import numpy as np cimport numpy as np import bezier cimport bezier._curve +cimport bezier._helpers logger = MLogger(__name__, level=1) @@ -77,7 +78,7 @@ cdef double calc_catmull_rom_one_point(double x, double v0, double v1, double v2 # 指定したすべての値をカトマル曲線として計算する -cdef np.ndarray calc_value_from_catmullrom(str bone_name, list fnos, list values): +cpdef np.ndarray calc_value_from_catmullrom(str bone_name, list fnos, list values): cdef np.ndarray[np.float_t, ndim=1] y_intpol cdef list prev_list, next_list cdef int fidx, sfno, efno, res @@ -136,8 +137,133 @@ cdef np.ndarray calc_value_from_catmullrom(str bone_name, list fnos, list values # 指定したすべての値を通るカトマル曲線からベジェ曲線を計算し、MMD補間曲線範囲内に収められた場合、そのベジェ曲線を返す def join_value_2_bezier(fno: int, bone_name: str, values: list, offset=0, diff_limit=0.01): - return_tuple = c_join_value_2_bezier(fno, bone_name, values, offset, diff_limit) - return return_tuple[0], return_tuple[1] + # return_tuple = c_join_value_2_bezier(fno, bone_name, values, offset, diff_limit) + # return return_tuple[0], return_tuple[1] + + if np.isclose(np.max(np.array(values)), np.min(np.array(values)), atol=1e-3) or len(values) <= 2: + # すべてがだいたい同じ値(最小と最大が同じ値)か次数が1の場合、線形補間 + return LINEAR_MMD_INTERPOLATION + + try: + # Xは次数(フレーム数)分移動 + xs = np.arange(0, len(values)) + # YはXの移動分を許容範囲とする + ys = values + xs[-1] + + # カトマル曲線をベジェ曲線に変換する + bz_x, bz_y = convert_catmullrom_2_bezier(np.concatenate([[None], xs, [None]]), np.concatenate([[None], ys, [None]])) + logger.test("bz_x: %s", bz_x) + logger.test("bz_y: %s", bz_y) + + if len(bz_x) == 0: + # 始点と終点が指定されていて、カトマル曲線が描けなかった場合、線形補間 + return LINEAR_MMD_INTERPOLATION + + # 次数 + degree = len(bz_x) - 1 + logger.test("degree: %s", degree) + + # すべての制御点を加味したベジェ曲線 + full_curve = bezier.Curve(np.asfortranarray([bz_x, bz_y]), degree=degree) + + if degree < 3: + # 3次未満の場合、3次まで次数を増やす + joined_curve = full_curve.elevate() + for _ in range(1, 3 - degree): + joined_curve = joined_curve.elevate() + elif degree == 3: + # 3次の場合、そのままベジェ曲線をMMD用に補間 + joined_curve = full_curve + elif degree > 3: + # 3次より多い場合、次数を減らす + + reduced_curve_list = [] + bz_x = full_curve.nodes[0] + bz_y = full_curve.nodes[1] + logger.test("START bz_x: %s, bz_y: %s", bz_x, bz_y) + + # 3次になるまでベジェ曲線を繋いで減らしていく + while len(bz_x) > 4: + reduced_curve_list = [] + + for n in range(0, degree + 1, 5): + reduce_bz_x = bz_x[n:n + 5] + reduce_bz_y = bz_y[n:n + 5] + logger.test("n: %s, reduce_bz_x: %s, reduce_bz_y: %s", n, reduce_bz_x, reduce_bz_y) + reduced_curve = bezier.Curve(np.asfortranarray([reduce_bz_x, reduce_bz_y]), degree=(len(reduce_bz_x) - 1)) + + # 次数がある場合、減らす + if (len(reduce_bz_x) - 1) > 1: + reduced_curve = reduced_curve.reduce_() + + logger.test("n: %s, nodes: %s", n, reduced_curve.nodes) + + # リストに追加 + reduced_curve_list.append(reduced_curve) + + bz_x = [] + bz_y = [] + + for reduced_curve in reduced_curve_list: + bz_x = np.append(bz_x, reduced_curve.nodes[0]) + bz_y = np.append(bz_y, reduced_curve.nodes[1]) + + logger.test("NEXT bz_x: %s, bz_y: %s", bz_x, bz_y) + + logger.test("FINISH bz_x: %s, bz_y: %s", bz_x, bz_y) + + # bz_x = [full_curve.nodes[0][0]] + list(bz_x) + [full_curve.nodes[0][-1]] + # bz_y = [full_curve.nodes[0][0]] + list(bz_y) + [full_curve.nodes[0][-1]] + + joined_curve = bezier.Curve(np.asfortranarray([bz_x, bz_y]), degree=(len(bz_x) - 1)) + + logger.test("joined_curve: %s", joined_curve.nodes) + + # 全体のキーフレ + bezier_x = np.arange(0, len(values))[1:-1] + + # 元の2つのベジェ曲線との交点を取得する + full_ys = intersect_by_x(full_curve, bezier_x) + logger.test("f: %s, %s, full_ys: %s", fno, bone_name, full_ys) + + # 次数を減らしたベジェ曲線との交点を取得する + reduced_ys = intersect_by_x(joined_curve, bezier_x) + logger.test("f: %s, %s, reduced_ys: %s", fno, bone_name, reduced_ys) + + # 交点の差を取得する + diff_ys = np.array(full_ys) - np.array(reduced_ys) + + # 差が大きい箇所をピックアップする + diff_large = np.where(np.abs(diff_ys) > (diff_limit * (offset + 1)), 1, 0) + + # 差が一定未満である場合、ベジェ曲線をMMD補間曲線に合わせる + nodes = joined_curve.nodes + + # MMD用補間曲線に変換 + joined_bz = scale_bezier(MVector2D(nodes[0, 0], nodes[1, 0]), MVector2D(nodes[0, 1], nodes[1, 1]), \ + MVector2D(nodes[0, 2], nodes[1, 2]), MVector2D(nodes[0, 3], nodes[1, 3])) + logger.debug("f: %s, %s, values: %s, nodes: %s, full_ys: %s, reduced_ys: %s, diff_ys: %s, diff_limit: %s, diff_large: %s, joined_bz: %s, %s, fit: %s", \ + fno, bone_name, values, joined_curve.nodes, full_ys, reduced_ys, diff_ys, diff_limit, np.count_nonzero(diff_large) > 0, joined_bz[1], joined_bz[2], \ + is_fit_bezier_mmd(joined_bz, offset)) + + if np.count_nonzero(diff_large) > 0: + # 差が大きい箇所がある場合、分割不可 + return None + + if not is_fit_bezier_mmd(joined_bz, offset): + # 補間曲線がMMD補間曲線内に収まらない場合、NG + return None + + # オフセット込みの場合、MMD用補間曲線枠内に収める + fit_bezier_mmd(joined_bz) + + # すべてクリアした場合、補間曲線採用 + return joined_bz + except Exception as e: + # エラーレベルは落として表に出さない + logger.debug("ベジェ曲線生成失敗", e) + return None + cdef tuple c_join_value_2_bezier(int fno, str bone_name, list values, double offset, double diff_limit): if len(values) <= 2: @@ -160,7 +286,7 @@ cdef tuple c_join_value_2_bezier(int fno, str bone_name, list values, double off # カトマル曲線をベジェ曲線に変換する (bz_x, bz_y) = convert_catmullrom_2_bezier(np.concatenate([[None], xs, [None]]), np.concatenate([[None], ys, [None]])) - logger.debug("bz_x: %s, bz_y: %s", bz_x, bz_y) + logger.debug("bz_x: %s, bz_y: %s", list(bz_x), list(bz_y)) if len(bz_x) == 0: # 始点と終点が指定されていて、カトマル曲線が描けなかった場合、線形補間 @@ -189,12 +315,14 @@ cdef tuple c_join_value_2_bezier(int fno, str bone_name, list values, double off bz_x = full_curve.nodes[0] bz_y = full_curve.nodes[1] logger.test("START bz_x: %s, bz_y: %s", bz_x, bz_y) + + reduced_nodes = bezier.hazmat.curve_helpers.full_reduce(full_curve.nodes) # 3次になるまでベジェ曲線を繋いで減らしていく - while len(bz_x) > 4: + while len(bz_x) > 5: reduced_curve_list = [] - for n in range(0, degree + 1, 5): + for n in range(0, degree + 1, 4): reduce_bz_x = bz_x[n:n + 5] reduce_bz_y = bz_y[n:n + 5] logger.test("n: %s, reduce_bz_x: %s, reduce_bz_y: %s", n, reduce_bz_x, reduce_bz_y) @@ -213,17 +341,20 @@ cdef tuple c_join_value_2_bezier(int fno, str bone_name, list values, double off bz_y = np.empty(0) for reduced_curve in reduced_curve_list: - bz_x = np.append(bz_x, reduced_curve.nodes[0]) - bz_y = np.append(bz_y, reduced_curve.nodes[1]) + bz_x = np.append(bz_x, reduced_curve.nodes[0, -1]) + bz_y = np.append(bz_y, reduced_curve.nodes[1, -1]) logger.test("NEXT bz_x: %s, bz_y: %s", bz_x, bz_y) + + reduced_curve = bezier.Curve(np.asfortranarray([bz_x, bz_y]), degree=(len(bz_x) - 1)) + joined_curve = reduced_curve.reduce_() logger.test("FINISH bz_x: %s, bz_y: %s", bz_x, bz_y) # bz_x = [full_curve.nodes[0][0]] + list(bz_x) + [full_curve.nodes[0][-1]] # bz_y = [full_curve.nodes[0][0]] + list(bz_y) + [full_curve.nodes[0][-1]] - joined_curve = bezier.Curve(np.asfortranarray([bz_x, bz_y]), degree=(len(bz_x) - 1)) + # joined_curve = bezier.Curve(np.asfortranarray([bz_x, bz_y]), degree=(len(bz_x) - 1)) logger.test("joined_curve: %s", joined_curve.nodes) @@ -247,9 +378,22 @@ cdef tuple c_join_value_2_bezier(int fno, str bone_name, list values, double off # 差が一定未満である場合、ベジェ曲線をMMD補間曲線に合わせる nodes = joined_curve.nodes + # 標準化 + x_min = np.min(nodes[0]) + x_max = np.max(nodes[0]) + y_min = np.min(nodes[1]) + y_max = np.max(nodes[1]) + + x_diff = x_max - x_min + y_diff = y_max - y_min + + p1 = MVector2D((nodes[0, 0] - x_min) / x_diff, (nodes[1, 0] - y_min) / y_diff) + p2 = MVector2D((nodes[0, 1] - x_min) / x_diff, (nodes[1, 1] - y_min) / y_diff) + p3 = MVector2D((nodes[0, 2] - x_min) / x_diff, (nodes[1, 2] - y_min) / y_diff) + p4 = MVector2D((nodes[0, 3] - x_min) / x_diff, (nodes[1, 3] - y_min) / y_diff) + # MMD用補間曲線に変換 - joined_bz = scale_bezier(MVector2D(nodes[0, 0], nodes[1, 0]), MVector2D(nodes[0, 1], nodes[1, 1]), \ - MVector2D(nodes[0, 2], nodes[1, 2]), MVector2D(nodes[0, 3], nodes[1, 3])) + joined_bz = scale_bezier(p1, p2, p3, p4) logger.debug("f: %s, %s, values: %s, nodes: %s, full_ys: %s, reduced_ys: %s, diff_ys: %s, diff_limit: %s, diff_large: %s, joined_bz: %s, %s, fit: %s", \ fno, bone_name, values, joined_curve.nodes, full_ys, reduced_ys, diff_ys, diff_limit, np.count_nonzero(diff_large) > 0, joined_bz[1], joined_bz[2], \ is_fit_bezier_mmd(joined_bz, offset)) @@ -260,18 +404,16 @@ cdef tuple c_join_value_2_bezier(int fno, str bone_name, list values, double off if not is_fit_bezier_mmd(joined_bz, offset): # 補間曲線がMMD補間曲線内に収まらない場合、NG + f_prime = np.gradient(values) + sign = np.concatenate([[0], np.diff(np.sign(np.diff(f_prime))), [0]]) + np_indices = np.where(np.abs(sign) > 1)[0] + + logger.debug("%s: f: %s, values: %s", bone_name, fno, values) + logger.debug("%s: f: %s, f_prime: %s", bone_name, fno, f_prime) + logger.debug("%s: f: %s, sign: %s", bone_name, fno, sign) + logger.debug("%s: f: %s, np_indices: %s", bone_name, fno, np_indices) - # 差分の大きなところを返す - diff_large = np.where(np.abs(diff_ys) > (diff_limit * 0.5 * (offset + 1)), 1, 0).astype(np.float) - if np.count_nonzero(diff_large) > 0: - return (None, np.where(diff_large)[0].tolist()) - - # 差分の大きなところを返す - diff_large = np.where(np.abs(diff_ys) > 0, 1, 0).astype(np.float) - if np.count_nonzero(diff_large) > 0: - return (None, np.where(diff_large)[0].tolist()) - - return (None, []) + return (None, np_indices) # オフセット込みの場合、MMD用補間曲線枠内に収める fit_bezier_mmd(joined_bz) @@ -314,8 +456,8 @@ cdef tuple convert_catmullrom_2_bezier(np.ndarray xs, np.ndarray ys): continue if not p0 and p3: - bz_x.append(p1.x()) - bz_y.append(p1.y()) + # bz_x.append(p1.x()) + # bz_y.append(p1.y()) # p0が空の場合、始点 B = (p1 * (1 / 2)) - p2 + (p3 * (1 / 2)) @@ -344,8 +486,8 @@ cdef tuple convert_catmullrom_2_bezier(np.ndarray xs, np.ndarray ys): bz_y.append(s1.y()) bz_y.append(s2.y()) - bz_x.append(xs[-2]) - bz_y.append(ys[-2]) + # bz_x.append(xs[-2]) + # bz_y.append(ys[-2]) return (np.array(bz_x, dtype=np.float64), np.array(bz_y, dtype=np.float64)) diff --git a/src/utils/MFileutils.py b/src/utils/MFileutils.py index 7f822fd..4e31ee7 100644 --- a/src/utils/MFileutils.py +++ b/src/utils/MFileutils.py @@ -194,7 +194,7 @@ def is_auto_vmd_output_path(output_vmd_path: str, motion_vmd_dir_path: str, moti # base_file_path: モーションカメラVMDパス # rep_pmx_path: 変換先モデルPMXパス # output_camera_vmd_path: 出力ファイルパス -def get_output_camera_vmd_path(base_file_path: str, rep_pmx_path: str, output_camera_vmd_path: str, is_force=False): +def get_output_camera_vmd_path(base_file_path: str, rep_pmx_path: str, output_camera_vmd_path: str, camera_length: float, is_force=False): # モーションカメラVMDパスの拡張子リスト if not os.path.exists(base_file_path) or not os.path.exists(rep_pmx_path): return "" @@ -207,7 +207,8 @@ def get_output_camera_vmd_path(base_file_path: str, rep_pmx_path: str, output_ca rep_pmx_file_name, _ = os.path.splitext(os.path.basename(rep_pmx_path)) # 出力ファイルパス生成 - new_output_camera_vmd_path = os.path.join(motion_camera_vmd_dir_path, "{0}_{1}_{2:%Y%m%d_%H%M%S}{3}".format(motion_camera_vmd_file_name, rep_pmx_file_name, datetime.now(), ".vmd")) + new_output_camera_vmd_path = os.path.join(motion_camera_vmd_dir_path, "{0}_{1}({2})_{3:%Y%m%d_%H%M%S}{4}".format( \ + motion_camera_vmd_file_name, rep_pmx_file_name, camera_length, datetime.now(), ".vmd")) # ファイルパス自体が変更されたか、自動生成ルールに則っている場合、ファイルパス変更 if is_force or is_auto_camera_vmd_output_path(output_camera_vmd_path, motion_camera_vmd_dir_path, motion_camera_vmd_file_name, ".vmd", rep_pmx_file_name): @@ -238,7 +239,7 @@ def is_auto_camera_vmd_output_path(output_camera_vmd_path: str, motion_camera_vm escaped_rep_pmx_file_name = escape_filepath(rep_pmx_file_name) escaped_motion_camera_vmd_ext = escape_filepath(motion_camera_vmd_ext) - new_output_camera_vmd_pattern = re.compile(r'^%s_%s%s%s$' % (escaped_motion_camera_vmd_file_name, \ + new_output_camera_vmd_pattern = re.compile(r'^%s_%s(\d+)_%s%s$' % (escaped_motion_camera_vmd_file_name, \ escaped_rep_pmx_file_name, r"_\d{8}_\d{6}", escaped_motion_camera_vmd_ext)) # 自動生成ルールに則ったファイルパスである場合、合致あり diff --git a/vmdising_np64.spec b/vmdising_np64.spec index b66477e..5d97829 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,7 +26,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β55_64bit', + name='VmdSizing_5.01_β56_64bit', debug=False, bootloader_ignore_signals=False, strip=False, From 767152075aa60a51f514b6e67d48a055728c031c Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Sat, 24 Oct 2020 07:41:09 +0900 Subject: [PATCH 23/37] =?UTF-8?q?5.01=5F=CE=B257?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VmdSizing_5.01_β57 miumiu ・カメラ  ・カメラが急にアップになってしまう不具合に対応   主に後ろ向きで上半身が映っている場合に、目ボーンが手前にあると誤判定 --- src/executor.py | 2 +- src/service/parts/CameraService.py | 69 +++++++++++++++++++++++------- vmdising_np64.spec | 2 +- 3 files changed, 55 insertions(+), 18 deletions(-) diff --git a/src/executor.py b/src/executor.py index 20d11a9..2848b23 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00_β56" +VERSION_NAME = "ver5.00_β57" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/service/parts/CameraService.py b/src/service/parts/CameraService.py index e856708..2b24d17 100644 --- a/src/service/parts/CameraService.py +++ b/src/service/parts/CameraService.py @@ -241,11 +241,11 @@ def calc_camera_ratio(self, fno: int, cf: VmdCameraFrame): # 画面端ボーンを計算する(体幹が取れている場合、体幹ベースで画面端を取る。体幹ベースは少し離れていても対象とする) if len(org_trunk_inner_square_poses.keys()) > 0: - (top_data_set_idx, top_bone_name), (bottom_data_set_idx, bottom_bone_name) = self.calc_top_botom_index(fno, cf, org_trunk_inner_square_poses, 0, 0.1) + (top_data_set_idx, top_bone_name), (bottom_data_set_idx, bottom_bone_name) = self.calc_top_botom_index(fno, cf, org_trunk_inner_square_poses, org_inner_global_poses, 0, 0.1) else: - (top_data_set_idx, top_bone_name), (bottom_data_set_idx, bottom_bone_name) = self.calc_top_botom_index(fno, cf, org_inner_square_poses, 0, 0.1) + (top_data_set_idx, top_bone_name), (bottom_data_set_idx, bottom_bone_name) = self.calc_top_botom_index(fno, cf, org_inner_square_poses, org_inner_global_poses, 0, 0.1) - (left_data_set_idx, left_bone_name), (right_data_set_idx, right_bone_name) = self.calc_left_right_index(fno, cf, org_inner_square_poses, 0, 0.1) + (left_data_set_idx, left_bone_name), (right_data_set_idx, right_bone_name) = self.calc_left_right_index(fno, cf, org_inner_square_poses, org_inner_global_poses, 0, 0.1) org_top_diff = rep_top_diff = org_bottom_diff = rep_bottom_diff = 0 @@ -327,7 +327,8 @@ def calc_inner_index(self, fno: int, all_global_poses: dict, all_square_poses: d return inner_global_poses, inner_square_poses # 画面端に最も近いINDEXを返す - def calc_top_botom_index(self, fno: int, cf: VmdCameraFrame, all_square_poses: dict, x_offset: float, y_offset: float): + def calc_top_botom_index(self, fno: int, cf: VmdCameraFrame, all_square_poses: dict, org_inner_global_poses: dict, x_offset: float, y_offset: float): + square_keys = list(all_square_poses.keys()) square_poses = np.array(list(all_square_poses.values())) if len(square_poses) == 0: @@ -344,35 +345,61 @@ def calc_top_botom_index(self, fno: int, cf: VmdCameraFrame, all_square_poses: d return (-1, ""), (-1, "") y_indexes = np.argsort(square_poses[refrected_indexes][:, 1]) + logger.debug("f: %s, y_indexes: %s", fno, y_indexes) + # 画面上端(Y最小) top_edge_index = refrected_indexes[0][y_indexes[0]] top_edge_pos = square_poses[top_edge_index] + top_edge_key = square_keys[top_edge_index] + top_edge_global_pos = org_inner_global_poses[top_edge_key] + + # 画面下端(Y最大) + bottom_edge_index = refrected_indexes[0][y_indexes[-1]] + bottom_edge_pos = square_poses[bottom_edge_index] + bottom_edge_key = square_keys[bottom_edge_index] + bottom_edge_global_pos = org_inner_global_poses[bottom_edge_key] + + # 上下のグローバル距離 + global_distance = MVector3D(top_edge_global_pos).distanceToPoint(MVector3D(bottom_edge_global_pos)) / 10 + for yi in y_indexes: - if square_poses[yi][1] > square_poses[refrected_indexes[0][y_indexes[0]]][1] + 0.2: + if square_poses[yi][1] > square_poses[refrected_indexes[0][y_indexes[0]]][1] + 0.1: # 上端から画面0.2の距離までなめたら終了 + logger.debug("f: %s, 画面上端 break: yi: %s, top: %s", fno, yi, square_poses[refrected_indexes[0][y_indexes[0]]][1]) break - if square_poses[yi][2] < top_edge_pos[2]: + logger.debug("f: %s, 画面 check: square_poses[yi][1]: %s, square_poses[yi][2]: %s, top_edge_pos[1]: %s, top_edge_pos[2]: %s", \ + fno, square_poses[yi][1], square_poses[yi][2], top_edge_pos[1], top_edge_pos[2]) + + top_target_key = (int(square_keys[yi][0]), str(square_keys[yi][1])) + top_target_global_pos = org_inner_global_poses[top_target_key] + + if top_target_global_pos[2] + global_distance < top_edge_global_pos[2]: # より前にある場合、そちらを採用 top_edge_pos = square_poses[yi] top_edge_index = yi - # 画面下端(Y最大) - bottom_edge_index = refrected_indexes[0][y_indexes[-1]] - bottom_edge_pos = square_poses[bottom_edge_index] for yi in reversed(y_indexes): if square_poses[yi][1] < square_poses[refrected_indexes[0][y_indexes[-1]]][1] - 0.2: # 下端から画面0.2の距離までなめたら終了 + logger.debug("f: %s, 画面下端 break: yi: %s, top: %s", fno, yi, square_poses[refrected_indexes[0][y_indexes[-1]]][1]) break - if square_poses[yi][2] < bottom_edge_pos[2]: + logger.debug("f: %s, 画面 check: square_poses[yi][1]: %s, square_poses[yi][2]: %s, top_edge_pos[1]: %s, top_edge_pos[2]: %s", \ + fno, square_poses[yi][1], square_poses[yi][2], bottom_edge_pos[1], bottom_edge_pos[2]) + + bottom_target_key = (int(square_keys[yi][0]), str(square_keys[yi][1])) + bottom_target_global_pos = org_inner_global_poses[bottom_target_key] + + if bottom_target_global_pos[2] + global_distance < bottom_edge_global_pos[2]: # より前にある場合、そちらを採用 bottom_edge_pos = square_poses[yi] bottom_edge_index = yi return list(all_square_poses.keys())[top_edge_index], list(all_square_poses.keys())[bottom_edge_index] - def calc_left_right_index(self, fno: int, cf: VmdCameraFrame, all_square_poses: dict, x_offset: float, y_offset: float): + def calc_left_right_index(self, fno: int, cf: VmdCameraFrame, all_square_poses: dict, org_inner_global_poses: dict, x_offset: float, y_offset: float): + square_keys = list(all_square_poses.keys()) square_poses = np.array(list(all_square_poses.values())) if len(square_poses) == 0: @@ -389,28 +416,38 @@ def calc_left_right_index(self, fno: int, cf: VmdCameraFrame, all_square_poses: return (-1, None), (-1, None) x_indexes = np.argsort(square_poses[refrected_indexes][:, 0]) + # 画面左端(X最小) left_edge_index = refrected_indexes[0][x_indexes[0]] left_edge_pos = square_poses[left_edge_index] + left_edge_key = square_keys[left_edge_index] + left_edge_global_pos = org_inner_global_poses[left_edge_key] + + # 画面右端(X最大) + right_edge_index = refrected_indexes[0][x_indexes[-1]] + right_edge_pos = square_poses[right_edge_index] + right_edge_key = square_keys[right_edge_index] + right_edge_global_pos = org_inner_global_poses[right_edge_key] + + # 左右のグローバル距離 + global_distance = MVector3D(left_edge_global_pos).distanceToPoint(MVector3D(right_edge_global_pos)) / 10 + for xi in x_indexes: if square_poses[xi][0] > square_poses[refrected_indexes[0][x_indexes[0]]][0] + 0.1: # 左端から画面1/10の距離までなめたら終了 break - if square_poses[xi][2] < left_edge_pos[2]: + if square_poses[xi][2] + global_distance < left_edge_pos[2]: # より前にある場合、そちらを採用 left_edge_pos = square_poses[xi] left_edge_index = xi - # 画面右端(X最大) - right_edge_index = refrected_indexes[0][x_indexes[-1]] - right_edge_pos = square_poses[right_edge_index] for xi in reversed(x_indexes): if square_poses[xi][0] < square_poses[refrected_indexes[0][x_indexes[-1]]][0] - 0.1: # 右端から画面1/10の距離までなめたら終了 break - if square_poses[xi][2] < right_edge_pos[2]: + if square_poses[xi][2] + global_distance < right_edge_pos[2]: # より前にある場合、そちらを採用 right_edge_pos = square_poses[xi] right_edge_index = xi diff --git a/vmdising_np64.spec b/vmdising_np64.spec index 5d97829..83f52fd 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,7 +26,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β56_64bit', + name='VmdSizing_5.01_β57_64bit', debug=False, bootloader_ignore_signals=False, strip=False, From 1b807117c9ce7e31bc611065df8cc0f3b6ff81a6 Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Sun, 25 Oct 2020 09:48:24 +0900 Subject: [PATCH 24/37] =?UTF-8?q?5.01=5F=CE=B258?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VmdSizing_5.01_β58 miumiu ・カメラ  ・カメラの角度をラジアン角からオイラー角に変換し忘れてた箇所があったのを修正  ・前キーフレの距離と視野角を保持し忘れてて、カメラの距離・視野角コピーの判定に常に失敗していたのを修正  ・Yオフセットを、先モデルに入れちゃってたので、元モデルで考慮するよう修正  ・Yオフセットの説明をツールチップに追加 ・CSV出力  ・カメラの出力にオイラー角を入れ忘れていたのを修正 --- src/executor.py | 2 +- src/form/panel/CameraPanel.py | 4 +- src/mmd/VmdData.pyx | 16 +++++ src/mmd/VmdReader.py | 4 +- src/service/ConvertCsvService.py | 5 +- src/service/SizingService.py | 4 ++ src/service/parts/CameraService.py | 103 ++++++++++++++++++++--------- vmdising_np64.spec | 2 +- 8 files changed, 102 insertions(+), 38 deletions(-) diff --git a/src/executor.py b/src/executor.py index 2848b23..be0c89a 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00_β57" +VERSION_NAME = "ver5.00_β58" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/form/panel/CameraPanel.py b/src/form/panel/CameraPanel.py index f29bd90..022871c 100644 --- a/src/form/panel/CameraPanel.py +++ b/src/form/panel/CameraPanel.py @@ -206,7 +206,9 @@ def __init__(self, frame: wx.Frame, panel: wx.Panel, window: wx.Window, set_idx: # オフセットYコントロール self.camera_offset_y_ctrl = wx.SpinCtrlDouble(self.window, id=wx.ID_ANY, size=wx.Size(100, -1), value="0.0", min=-1000, max=1000, initial=0.0, inc=0.1) - self.camera_offset_y_ctrl.SetToolTip(u"カメラに映す変換先モデルの全長を調整するオフセット値を指定できます。") + self.camera_offset_y_ctrl.SetToolTip(u"カメラに映す変換先モデルの全長を調整するオフセット値を指定できます。\n" \ + + "髪飾り等、「頭頂部より上にあるオブジェクトを除外したい」場合、マイナス値を指定して下さい。\n" \ + + "アホ毛等、「頭頂部より上にあるオブジェクトを含めたい」場合、プラス値を指定して下さい。") self.camera_offset_y_ctrl.Bind(wx.EVT_MOUSEWHEEL, lambda event: self.frame.on_wheel_spin_ctrl(event, 0.2)) self.offset_sizer.Add(self.camera_offset_y_ctrl, 0, wx.ALL, 5) diff --git a/src/mmd/VmdData.pyx b/src/mmd/VmdData.pyx index dd8885b..80ce9e0 100644 --- a/src/mmd/VmdData.pyx +++ b/src/mmd/VmdData.pyx @@ -204,6 +204,7 @@ class VmdCameraFrame: self.perspective = 0 self.org_length = 0 self.org_position = MVector3D(0, 0, 0) + self.ratio = 0 def write(self, fout): fout.write(struct.pack('= 0: @@ -63,7 +64,7 @@ def execute(self): = self.calc_camera_ratio(fno, cf) # カメラサイジング実行 - self.execute_rep_camera(fno, cf, org_inner_global_poses, org_inner_square_poses, rep_inner_global_poses, ratio, nearest_data_set_idx, nearest_bone_name, \ + self.execute_rep_camera(fno, cf, past_cf, org_inner_global_poses, org_inner_square_poses, rep_inner_global_poses, ratio, nearest_data_set_idx, nearest_bone_name, \ left_data_set_idx, left_bone_name, right_data_set_idx, right_bone_name, top_data_set_idx, top_bone_name, bottom_data_set_idx, bottom_bone_name, \ self.options.camera_length) @@ -87,15 +88,14 @@ def execute(self): raise e # 変換先モデル用カメラ作成 - def execute_rep_camera(self, fno: int, cf: VmdCameraFrame, org_inner_global_poses: dict, org_inner_square_poses: dict, rep_inner_global_poses: dict, \ + def execute_rep_camera(self, fno: int, cf: VmdCameraFrame, past_cf: VmdCameraFrame, org_inner_global_poses: dict, org_inner_square_poses: dict, rep_inner_global_poses: dict, \ ratio: float, nearest_data_set_idx: int, nearest_bone_name: str, left_data_set_idx: int, left_bone_name: str, right_data_set_idx: int, right_bone_name: str, \ top_data_set_idx: int, top_bone_name: str, bottom_data_set_idx: int, bottom_bone_name: str, camera_length: float): - # ---------------- - # 画面内に映ってるボーンINDEXの中央値を仮の中央座標とする - org_mean_vec = MVector3D(np.mean(np.array(list(org_inner_global_poses.values())), axis=0)) + # ------------------------ # 画面内に映ってるボーンINDEXの中央値を仮の中央座標とする + org_mean_vec = MVector3D(np.mean(np.array(list(org_inner_global_poses.values())), axis=0)) rep_mean_vec = MVector3D(np.mean(np.array(list(rep_inner_global_poses.values())), axis=0)) # カメラ角度 @@ -111,13 +111,13 @@ def execute_rep_camera(self, fno: int, cf: VmdCameraFrame, org_inner_global_pose camera_origin = mat_origin * MVector3D() logger.test("camera_origin: %s", camera_origin) - # 最も近いボーンの相対位置 - org_nearest_relative_vec = (camera_origin - org_mean_vec) * ratio + # ボーンの相対位置 + org_nearest_relative_vec = (camera_origin - org_mean_vec) logger.debug("org_mean_vec: %s", org_mean_vec.to_log()) logger.debug("org_nearest_relative_vec: %s", org_nearest_relative_vec.to_log()) # 距離0の場合のカメラの位置を算出 - cf_pos = rep_mean_vec + org_nearest_relative_vec + cf_pos = rep_mean_vec + (org_nearest_relative_vec * ratio) logger.debug("cf_pos: %s", cf_pos) mat_len = MMatrix4x4() @@ -137,9 +137,12 @@ def execute_rep_camera(self, fno: int, cf: VmdCameraFrame, org_inner_global_pose limit_ratio = min(camera_length, max(1 / camera_length, ratio)) if camera_length < 5 else ratio cf.length = cf.length * limit_ratio + # 比率を保持 + cf.ratio = limit_ratio + offset_length = 0 offset_angle = 0 - + # ------------------------ rep_inner_square_poses = {} @@ -150,8 +153,8 @@ def execute_rep_camera(self, fno: int, cf: VmdCameraFrame, org_inner_global_pose rep_bottom_pos = rep_inner_global_poses[(bottom_data_set_idx, bottom_bone_name)] # 先モデル上下プロジェクション正規位置 - rep_inner_square_poses[(top_data_set_idx, top_bone_name)] = self.calc_project_square_pos(cf, MVector3D(rep_top_pos)).data() - rep_inner_square_poses[(bottom_data_set_idx, bottom_bone_name)] = self.calc_project_square_pos(cf, MVector3D(rep_bottom_pos)).data() + rep_inner_square_poses[(top_data_set_idx, top_bone_name)] = self.calc_project_square_vec(cf, MVector3D(rep_top_pos)).data() + rep_inner_square_poses[(bottom_data_set_idx, bottom_bone_name)] = self.calc_project_square_vec(cf, MVector3D(rep_bottom_pos)).data() # 上下のY差 org_diff = org_inner_square_poses[(bottom_data_set_idx, bottom_bone_name)][1] - org_inner_square_poses[(top_data_set_idx, top_bone_name)][1] @@ -174,18 +177,18 @@ def execute_rep_camera(self, fno: int, cf: VmdCameraFrame, org_inner_global_pose offset_angle += 1 # 先モデル上下プロジェクション正規位置 - rep_inner_square_poses[(top_data_set_idx, top_bone_name)] = self.calc_project_square_pos(cf, MVector3D(rep_top_pos)).data() - rep_inner_square_poses[(bottom_data_set_idx, bottom_bone_name)] = self.calc_project_square_pos(cf, MVector3D(rep_bottom_pos)).data() + rep_inner_square_poses[(top_data_set_idx, top_bone_name)] = self.calc_project_square_vec(cf, MVector3D(rep_top_pos)).data() + rep_inner_square_poses[(bottom_data_set_idx, bottom_bone_name)] = self.calc_project_square_vec(cf, MVector3D(rep_bottom_pos)).data() # 上下のY差 rep_diff = rep_inner_square_poses[(bottom_data_set_idx, bottom_bone_name)][1] - rep_inner_square_poses[(top_data_set_idx, top_bone_name)][1] cnt += 1 - logger.info("%sフレーム目 縮尺比率: %s, 注視点: %s-%s, 上辺: %s-%s, 下辺: %s-%s, 座標: %s, 距離: %s, 視野角: %s", \ + logger.info("%sフレーム目 縮尺比率: %s, 注視点: %s-%s, 上辺: %s-%s, 下辺: %s-%s, 距離オフセット: %s, 視野角オフセット: %s", \ cf.fno, round(limit_ratio, 5), (nearest_data_set_idx + 1), nearest_bone_name.replace("実体", ""), \ (top_data_set_idx + 1), top_bone_name.replace("実体", ""), (bottom_data_set_idx + 1), bottom_bone_name.replace("実体", ""), \ - org_nearest_relative_vec.to_log(), round(offset_length, 5), offset_angle) + round(offset_length, 5), offset_angle) # カメラ倍率計算 def calc_camera_ratio(self, fno: int, cf: VmdCameraFrame): @@ -488,10 +491,10 @@ def calc_org_project_square_poses(self, fno: int, cf: VmdCameraFrame, start_idx: # 元モデルのそれぞれのグローバル位置 org_global_3ds = MServiceUtils.calc_global_pos(data_set.camera_org_model, org_link, data_set.org_motion, fno) for bone_name, org_vec in org_global_3ds.items(): - if bone_name in camera_option.org_link_target.keys(): + if bone_name in camera_option.org_link_target.keys() and (data_set_idx, bone_name) not in all_org_project_square_poses: # 処理対象ボーンである場合、データを保持 all_org_global_poses[(data_set_idx, bone_name)] = org_vec.data() - all_org_project_square_poses[(data_set_idx, bone_name)] = self.calc_project_square_pos(cf, org_vec).data() + all_org_project_square_poses[(data_set_idx, bone_name)] = self.calc_project_square_vec(cf, org_vec).data() [logger.test("f: %s, k: %s, v: %s, s: %s", fno, k, v, sv) for (k, v), (sk, sv) in zip(all_org_global_poses.items(), all_org_project_square_poses.items())] @@ -523,8 +526,43 @@ def calc_rep_global_poses(self, fno: int, data_bone_name_list: list): return rep_global_poses + # プロジェクション座標からグローバル座標の位置際算出 + def calc_unproject_vec_from_square(self, cf: VmdCameraFrame, square_vec: MVector3D): + # モデル座標系 + model_view = self.create_model_view(cf) + + # プロジェクション座標系 + projection_view = self.create_projection_view(cf) + + # viewport + viewport_rect = MRect(0, 0, 16, 9) + + # 画面サイズに広げる + project_vec = square_vec * MVector3D(16, 9, 1) + + # グローバル座標位置 + grobal_vec = project_vec.unproject(model_view, projection_view, viewport_rect) + + return grobal_vec + + # プロジェクション座標正規位置算出 + def calc_project_vec(self, cf: VmdCameraFrame, global_vec: MVector3D): + # モデル座標系 + model_view = self.create_model_view(cf) + + # プロジェクション座標系 + projection_view = self.create_projection_view(cf) + + # viewport + viewport_rect = MRect(0, 0, 16, 9) + + # プロジェクション座標位置 + project_vec = global_vec.project(model_view, projection_view, viewport_rect) + + return project_vec + # プロジェクション座標正規位置算出 - def calc_project_square_pos(self, cf: VmdCameraFrame, global_vec: MVector3D): + def calc_project_square_vec(self, cf: VmdCameraFrame, global_vec: MVector3D): # モデル座標系 model_view = self.create_model_view(cf) @@ -562,7 +600,7 @@ def create_projection_view(self, cf: VmdCameraFrame): def calc_camera_qq(self, cf: VmdCameraFrame): # カメラ角度 - camera_qq = MQuaternion.fromEulerAngles(-cf.euler.x(), cf.euler.y(), cf.euler.z()) + camera_qq = MQuaternion.fromEulerAngles(-math.degrees(cf.euler.x()), math.degrees(cf.euler.y()), math.degrees(cf.euler.z())) camera_qq.setX(-camera_qq.x()) camera_qq.setScalar(-camera_qq.scalar()) @@ -611,19 +649,17 @@ def prepare(self, data_set_idx: int): rep_links = {} # 体幹系はループ外で定義する - self.prepare_link(data_set.camera_org_model, data_set.rep_model, org_links, org_link_target, rep_links, ["頭頂実体", "頭"], ["頭頂実体", "頭", "首", "首根元", "上半身2", "上半身"]) - self.prepare_link(data_set.camera_org_model, data_set.rep_model, org_links, org_link_target, rep_links, ["左目", "頭"], ["左目"]) - self.prepare_link(data_set.camera_org_model, data_set.rep_model, org_links, org_link_target, rep_links, ["右目", "頭"], ["右目"]) + self.prepare_link(data_set.camera_org_model, data_set.rep_model, data_set.camera_offset_y, org_links, org_link_target, rep_links, ["頭頂実体", "頭"], ["頭頂実体", "頭", "首", "首根元", "上半身2", "上半身"]) + self.prepare_link(data_set.camera_org_model, data_set.rep_model, data_set.camera_offset_y, org_links, org_link_target, rep_links, ["左目", "頭"], ["左目"]) + self.prepare_link(data_set.camera_org_model, data_set.rep_model, data_set.camera_offset_y, org_links, org_link_target, rep_links, ["右目", "頭"], ["右目"]) for direction in ["左", "右"]: - self.prepare_link(data_set.camera_org_model, data_set.rep_model, org_links, org_link_target, rep_links, ["{0}手首".format(direction), "{0}手首".format(direction)], \ - ["{0}手首".format(direction)]) - self.prepare_link(data_set.camera_org_model, data_set.rep_model, org_links, org_link_target, rep_links, ["{0}つま先実体".format(direction), "{0}足IK".format(direction)], \ - ["{0}つま先実体".format(direction), "{0}足底実体".format(direction), "{0}足IK".format(direction)]) - self.prepare_link(data_set.camera_org_model, data_set.rep_model, org_links, org_link_target, rep_links, ["{0}足".format(direction), "下半身"], ["{0}足".format(direction)]) - - # 頭頂実体をオフセット調整 - rep_links["頭頂実体"].get("頭頂実体").position.setY(float(rep_links["頭頂実体"].get("頭頂実体").position.y()) + float(data_set.camera_offset_y)) + self.prepare_link(data_set.camera_org_model, data_set.rep_model, data_set.camera_offset_y, org_links, org_link_target, rep_links, \ + ["{0}手首".format(direction), "{0}手首".format(direction)], ["{0}手首".format(direction)]) + self.prepare_link(data_set.camera_org_model, data_set.rep_model, data_set.camera_offset_y, org_links, org_link_target, rep_links, \ + ["{0}つま先実体".format(direction), "{0}足IK".format(direction)], ["{0}つま先実体".format(direction), "{0}足底実体".format(direction), "{0}足IK".format(direction)]) + self.prepare_link(data_set.camera_org_model, data_set.rep_model, data_set.camera_offset_y, org_links, org_link_target, rep_links, \ + ["{0}足".format(direction), "下半身"], ["{0}足".format(direction)]) self.camera_options[data_set_idx] = CameraOption(org_links, org_link_target, rep_links, org_total_height, org_face_length, org_heads, \ rep_total_height, rep_face_length, rep_heads, body_ratio, head_ratio) @@ -670,11 +706,16 @@ def calc_ratio(self, data_set_idx: int, model: PmxModel, model_type: str): # 顔の大きさ / 全身の高さ で頭身算出 return total_height, face_length, total_height / face_length - def prepare_link(self, org_model: PmxModel, rep_model: PmxModel, org_links: list, org_link_target: dict, rep_links: dict, link_bone_name_list: list, target_bone_name_list: list): + def prepare_link(self, org_model: PmxModel, rep_model: PmxModel, camera_offset_y: float, org_links: list, org_link_target: dict, rep_links: dict, link_bone_name_list: list, target_bone_name_list: list): if (link_bone_name_list[0] in org_model.bones and link_bone_name_list[0] in rep_model.bones) or \ (link_bone_name_list[1] in org_model.bones and link_bone_name_list[1] in rep_model.bones): # 元と先の両方に末端があればリンク作成 org_link = org_model.create_link_2_top_one(*link_bone_name_list) + + # 頭頂実体がある場合、Yオフセット加味 + if "頭頂実体" in list(org_link.all().keys()): + org_link.get("頭頂実体").position.setY(float(org_link.get("頭頂実体").position.y()) + float(camera_offset_y)) + # 先は、判定対象ボーンとそのボーンを生成するリンクのペアを登録する rep_target_bone_name_list = [] for target_bone_name in target_bone_name_list: diff --git a/vmdising_np64.spec b/vmdising_np64.spec index 83f52fd..f117323 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,7 +26,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β57_64bit', + name='VmdSizing_5.01_β58_64bit', debug=False, bootloader_ignore_signals=False, strip=False, From f72e3d0bb0831591d249ad5441e1c2386bbb856a Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Sun, 25 Oct 2020 17:05:57 +0900 Subject: [PATCH 25/37] =?UTF-8?q?5.01=5F=CE=B259?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VmdSizing_5.01_β59 miumiu ・カメラ  ・カメラ距離可動範囲の指定方法にプルダウンを追加  ・カメラの距離可動範囲に応じて、距離倍率と原点倍率をそれぞれ計算するよう調整 ・スムージング、モーフブレンド、補間パネルをモーションサポーターに移動 --- src/executor.py | 2 +- src/form/MainFrame.py | 25 -- src/form/panel/BezierPanel.py | 403 ----------------- src/form/panel/BlendPanel.py | 299 ------------- src/form/panel/CameraPanel.py | 23 +- src/form/panel/SmoothPanel.py | 370 ---------------- src/form/parts/FloatSliderCtrl.py | 3 + src/form/worker/BlendWorkerThread.py | 56 --- src/form/worker/SmoothWorkerThread.py | 59 --- src/module/MOptions.pxd | 18 - src/module/MOptions.pyx | 81 ---- src/service/ConvertSmoothService.py | 600 -------------------------- src/service/MorphBlendService.py | 159 ------- src/service/parts/CameraService.py | 62 +-- src/utils/MFileutils.py | 67 --- vmdising_np64.spec | 2 +- 16 files changed, 61 insertions(+), 2168 deletions(-) delete mode 100644 src/form/panel/BezierPanel.py delete mode 100644 src/form/panel/BlendPanel.py delete mode 100644 src/form/panel/SmoothPanel.py delete mode 100644 src/form/worker/BlendWorkerThread.py delete mode 100644 src/form/worker/SmoothWorkerThread.py delete mode 100644 src/service/ConvertSmoothService.py delete mode 100644 src/service/MorphBlendService.py diff --git a/src/executor.py b/src/executor.py index be0c89a..2a2ed6f 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00_β58" +VERSION_NAME = "ver5.00_β59" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/form/MainFrame.py b/src/form/MainFrame.py index 20f167c..c3fb6a6 100644 --- a/src/form/MainFrame.py +++ b/src/form/MainFrame.py @@ -10,11 +10,8 @@ from form.panel.MultiPanel import MultiPanel from form.panel.ArmPanel import ArmPanel from form.panel.CameraPanel import CameraPanel -from form.panel.BlendPanel import BlendPanel from form.panel.CsvPanel import CsvPanel from form.panel.VmdPanel import VmdPanel -from form.panel.BezierPanel import BezierPanel -from form.panel.SmoothPanel import SmoothPanel from form.worker.SizingWorkerThread import SizingWorkerThread from form.worker.LoadWorkerThread import LoadWorkerThread from module.MMath import MRect, MVector3D, MVector4D, MQuaternion, MMatrix4x4 # noqa @@ -99,14 +96,6 @@ def __init__(self, parent, mydir_path: str, version_name: str, logging_level: in self.camera_panel_ctrl = CameraPanel(self, self.note_ctrl, 4) self.note_ctrl.AddPage(self.camera_panel_ctrl, u"カメラ", False) - # スムーズタブ - self.smooth_panel_ctrl = SmoothPanel(self, self.note_ctrl, 5) - self.note_ctrl.AddPage(self.smooth_panel_ctrl, u"スムーズ", False) - - # ブレンドタブ - self.blend_panel_ctrl = BlendPanel(self, self.note_ctrl, 6) - self.note_ctrl.AddPage(self.blend_panel_ctrl, u"ブレンド", False) - # CSVタブ self.csv_panel_ctrl = CsvPanel(self, self.note_ctrl, 7) self.note_ctrl.AddPage(self.csv_panel_ctrl, u"CSV", False) @@ -115,10 +104,6 @@ def __init__(self, parent, mydir_path: str, version_name: str, logging_level: in self.vmd_panel_ctrl = VmdPanel(self, self.note_ctrl, 8) self.note_ctrl.AddPage(self.vmd_panel_ctrl, u"VMD", False) - # 補間タブ - self.bezier_panel_ctrl = BezierPanel(self, self.note_ctrl, 9) - self.note_ctrl.AddPage(self.bezier_panel_ctrl, u"補間", False) - # --------------------------------------------- # タブ押下時の処理 @@ -166,16 +151,6 @@ def on_tab_change(self, event: wx.Event): event.Skip() return - elif self.smooth_panel_ctrl.is_fix_tab: - self.note_ctrl.ChangeSelection(self.smooth_panel_ctrl.tab_idx) - event.Skip() - return - - elif self.blend_panel_ctrl.is_fix_tab: - self.note_ctrl.ChangeSelection(self.blend_panel_ctrl.tab_idx) - event.Skip() - return - elif self.csv_panel_ctrl.is_fix_tab: self.note_ctrl.ChangeSelection(self.csv_panel_ctrl.tab_idx) event.Skip() diff --git a/src/form/panel/BezierPanel.py b/src/form/panel/BezierPanel.py deleted file mode 100644 index d0a0974..0000000 --- a/src/form/panel/BezierPanel.py +++ /dev/null @@ -1,403 +0,0 @@ -# -*- coding: utf-8 -*- -# -import wx - -from form.panel.BasePanel import BasePanel -from form.parts.FloatSliderCtrl import FloatSliderCtrl -from module.MMath import MRect, MVector3D, MVector4D, MQuaternion, MMatrix4x4 # noqa -from utils import MFormUtils, MBezierUtils # noqa -from utils.MLogger import MLogger # noqa - -logger = MLogger(__name__, level=1) - - -class BezierPanel(BasePanel): - - def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int): - super().__init__(frame, parent, tab_idx) - - # 補間曲線の分割フレーム - self.slice_start = 0 - self.slice_end = 10 - self.slice_middle = 5 - - self.description_txt = wx.StaticText(self, wx.ID_ANY, "オリジナル補間曲線に指定された補間曲線を、指定された箇所で分割した場合の補間曲線を表示します。\n" \ - + "補間曲線は、数値で指定する他、MMDと同じくマウスで操作できます。\n補間曲線を正常に2分割できない場合、パネルに×が出ます。", \ - wx.DefaultPosition, wx.DefaultSize, 0) - self.sizer.Add(self.description_txt, 0, wx.ALL, 5) - - self.static_line01 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) - self.sizer.Add(self.static_line01, 0, wx.EXPAND | wx.ALL, 5) - - # 基本補間曲線 - self.base_sizer = wx.BoxSizer(wx.VERTICAL) - - self.base_bezier_title = wx.StaticText(self, wx.ID_ANY, u"オリジナル補間曲線", wx.DefaultPosition, wx.DefaultSize, 0) - self.base_bezier_title.SetToolTip(u"元々の分割したいキーフレの補間曲線を指定します。") - self.base_sizer.Add(self.base_bezier_title, 0, wx.ALL, 5) - - self.base_bezier = BezierBlock(self.frame, self, self.base_sizer) - self.sizer.Add(self.base_sizer, 0, wx.EXPAND | wx.ALL, 5) - - # 補間フレームの番号 --------------- - self.value_sizer = wx.BoxSizer(wx.HORIZONTAL) - - self.number_title_txt = wx.StaticText(self, wx.ID_ANY, u"キー分割フレーム: ", wx.DefaultPosition, wx.DefaultSize, 0) - self.number_title_txt.SetToolTip(u"キーをどの位置で分割するか指定して下さい。") - self.value_sizer.Add(self.number_title_txt, 0, wx.ALL, 5) - - self.number_value_txt = wx.StaticText(self, wx.ID_ANY, u"(" + str(self.slice_middle) + ")", wx.DefaultPosition, wx.DefaultSize, 0) - self.number_value_txt.SetToolTip(u"現在指定されているフレームの分割番号です。") - self.value_sizer.Add(self.number_value_txt, 0, wx.ALL, 5) - - self.sizer.Add(self.value_sizer, 0, wx.EXPAND | wx.ALL, 5) - - # 補間フレーム ---------------- - self.frame_sizer = wx.BoxSizer(wx.HORIZONTAL) - - # 補間の開始 - self.frame_start_ctrl = wx.SpinCtrl(self, id=wx.ID_ANY, size=wx.Size(80, -1), value=str(self.slice_start), min=0, max=999999999, initial=1) - self.frame_start_ctrl.Bind(wx.EVT_SPINCTRL, self.OnReCalcBezier) - self.frame_sizer.Add(self.frame_start_ctrl, 0, wx.EXPAND | wx.ALL, 5) - - # 補間を区切るフレーム - self.slice_slider_ctrl = FloatSliderCtrl(self, wx.ID_ANY, self.slice_middle, self.slice_start, self.slice_end, 1, self.number_value_txt, \ - wx.DefaultPosition, wx.DefaultSize, wx.SL_HORIZONTAL, scrollevt=self.OnRePaintBezier) - self.frame_sizer.Add(self.slice_slider_ctrl, 1, wx.ALL | wx.EXPAND, 5) - - # 補間の終了 - self.frame_end_ctrl = wx.SpinCtrl(self, id=wx.ID_ANY, size=wx.Size(80, -1), value=str(self.slice_end), min=0, max=999999999, initial=1) - self.frame_end_ctrl.Bind(wx.EVT_SPINCTRL, self.OnReCalcBezier) - self.frame_sizer.Add(self.frame_end_ctrl, 0, wx.EXPAND | wx.ALL, 5) - - self.sizer.Add(self.frame_sizer, 0, wx.EXPAND | wx.ALL, 5) - - # 分割フレーム ------------- - self.split_sizer = wx.BoxSizer(wx.HORIZONTAL) - - # 開始補間曲線 - self.split_before_sizer = wx.BoxSizer(wx.VERTICAL) - - self.before_bezier_title = wx.StaticText(self, wx.ID_ANY, u"分割キー補間曲線", wx.DefaultPosition, wx.DefaultSize, 0) - self.before_bezier_title.SetToolTip(u"分割したキーフレの補間曲線を表示します。") - self.split_before_sizer.Add(self.before_bezier_title, 0, wx.ALL, 5) - - self.before_bezier = BezierBlock(self.frame, self, self.split_before_sizer) - self.split_sizer.Add(self.split_before_sizer, 0, wx.EXPAND | wx.ALL, 5) - - # 終端補間曲線 - self.split_after_sizer = wx.BoxSizer(wx.VERTICAL) - - self.after_bezier_title = wx.StaticText(self, wx.ID_ANY, u"終端キー補間曲線", wx.DefaultPosition, wx.DefaultSize, 0) - self.after_bezier_title.SetToolTip(u"終わりのキーフレの補間曲線を表示します。") - self.split_after_sizer.Add(self.after_bezier_title, 0, wx.ALL, 5) - - self.after_bezier = BezierBlock(self.frame, self, self.split_after_sizer) - self.split_sizer.Add(self.split_after_sizer, 0, wx.EXPAND | wx.ALL, 5) - - self.sizer.Add(self.split_sizer, 0, wx.EXPAND | wx.ALL, 5) - - self.fit() - - # ベジェ曲線の再計算 ----------------- - def OnReCalcBezier(self, event): - self.slice_slider_ctrl.SetMin(float(self.frame_start_ctrl.GetValue())) - self.slice_slider_ctrl.SetMax(float(self.frame_end_ctrl.GetValue())) - - def OnRePaintBezier(self, event): - self.splitBezier() # ベジェ分割 - self.frame.Refresh(False) - - def splitBezier(self): - # ベジェ曲線の分割 - t, x, y, bresult, aresult, before_bz, after_bz = \ - MBezierUtils.split_bezier_mmd(self.base_bezier.start_x.GetValue(), self.base_bezier.start_y.GetValue(), \ - self.base_bezier.end_x.GetValue(), self.base_bezier.end_y.GetValue(), \ - self.frame_start_ctrl.GetValue(), int(self.slice_slider_ctrl.GetValue()), self.frame_end_ctrl.GetValue()) - - if bresult: - # 前半のキー - self.before_bezier.panel.iserror = False - self.before_bezier.start_x.SetValue(int(before_bz[1].x())) - self.before_bezier.start_y.SetValue(int(before_bz[1].y())) - self.before_bezier.end_x.SetValue(int(before_bz[2].x())) - self.before_bezier.end_y.SetValue(int(before_bz[2].y())) - else: - self.before_bezier.panel.iserror = True - - if aresult: - # 後半のキー - self.after_bezier.panel.iserror = False - self.after_bezier.start_x.SetValue(int(after_bz[1].x())) - self.after_bezier.start_y.SetValue(int(after_bz[1].y())) - self.after_bezier.end_x.SetValue(int(after_bz[2].x())) - self.after_bezier.end_y.SetValue(int(after_bz[2].y())) - else: - self.after_bezier.panel.iserror = True - - -class BezierBlock(): - def __init__(self, frame, parent, parent_sizer): - super().__init__() - - self.frame = frame - self.parent = parent - self.parent_sizer = parent_sizer - - self.panel_sizer = wx.BoxSizer(wx.HORIZONTAL) - - # 補間曲線パネル - self.panel = BezierViewPanel(self.parent, size=(130, 130)) - self.panel_sizer.Add(self.panel, 0, wx.ALL, 0) - - # 補間曲線値 - self.value_sizer = wx.FlexGridSizer(0, 2, 5, 0) - - # 開始X - self.start_x_title = wx.StaticText(self.parent, wx.ID_ANY, u"開始X: ", wx.DefaultPosition, wx.DefaultSize, 0) - self.value_sizer.Add(self.start_x_title, 0, wx.ALL, 5) - self.start_x = wx.SpinCtrl(self.parent, id=wx.ID_ANY, size=wx.Size(60, -1), value="20", min=0, max=127, initial=20) - self.start_x.Bind(wx.EVT_SPINCTRL, self.parent.OnRePaintBezier) - self.value_sizer.Add(self.start_x, 0, wx.ALL, 0) - # 開始Y - self.start_y_title = wx.StaticText(self.parent, wx.ID_ANY, u"開始Y: ", wx.DefaultPosition, wx.DefaultSize, 0) - self.value_sizer.Add(self.start_y_title, 0, wx.ALL, 5) - self.start_y = wx.SpinCtrl(self.parent, id=wx.ID_ANY, size=wx.Size(60, -1), value="20", min=0, max=127, initial=20) - self.start_y.Bind(wx.EVT_SPINCTRL, self.parent.OnRePaintBezier) - self.value_sizer.Add(self.start_y, 0, wx.ALL, 0) - # 終了X - self.end_x_title = wx.StaticText(self.parent, wx.ID_ANY, u"終了X: ", wx.DefaultPosition, wx.DefaultSize, 0) - self.value_sizer.Add(self.end_x_title, 0, wx.ALL, 5) - self.end_x = wx.SpinCtrl(self.parent, id=wx.ID_ANY, size=wx.Size(60, -1), value="107", min=0, max=127, initial=107) - self.end_x.Bind(wx.EVT_SPINCTRL, self.parent.OnRePaintBezier) - self.value_sizer.Add(self.end_x, 0, wx.ALL, 0) - # 終了Y - self.end_y_title = wx.StaticText(self.parent, wx.ID_ANY, u"終了Y: ", wx.DefaultPosition, wx.DefaultSize, 0) - self.value_sizer.Add(self.end_y_title, 0, wx.ALL, 5) - self.end_y = wx.SpinCtrl(self.parent, id=wx.ID_ANY, size=wx.Size(60, -1), value="107", min=0, max=127, initial=107) - self.end_y.Bind(wx.EVT_SPINCTRL, self.parent.OnRePaintBezier) - self.value_sizer.Add(self.end_y, 0, wx.ALL, 0) - - self.panel_sizer.Add(self.value_sizer, 0, wx.ALL, 0) - self.parent_sizer.Add(self.panel_sizer, 0, wx.ALL, 5) - - # ベジェ曲線描画 - self.panel.Bind(wx.EVT_PAINT, self.OnPaint) - self.panel.Bind(wx.EVT_LEFT_DOWN, self.OnPaintBezierMouseLeftDown) - self.panel.Bind(wx.EVT_LEFT_UP, self.OnPaintBezierMouseLeftUp) - self.panel.Bind(wx.EVT_MOTION, self.OnPaintBezierMouseMotion) - - # ベジェ曲線の描画 ------------------------- - def OnPaint(self, event: wx.Event): - dc = wx.PaintDC(self.panel) - dc = wx.BufferedDC(dc) # 画面に表示されないところで描画を行うためのデバイスコンテキストを作成 - m = Mapper(self.panel.GetSize(), (0, 0), (127, 127)) - target_ctrl = [(0, 0), (self.start_x.GetValue(), self.start_y.GetValue()), (self.end_x.GetValue(), self.end_y.GetValue()), (127, 127)] - - self.clearbezier(dc) - # self.drawgrid(target_ctrl, m, dc) - self.drawguide(target_ctrl[0], target_ctrl[1], self.start_x, self.start_y, m, dc) - self.drawguide(target_ctrl[2], target_ctrl[3], self.end_x, self.end_y, m, dc) - self.drawbezier(target_ctrl, m, dc) - self.drawbeziererror(m, dc) - - def clearbezier(self, dc): - self.setColor(dc, 'white') - dc.DrawRectangle(*(0, 0) + tuple(self.panel.GetSize())) - - def drawbezier(self, target_ctrl, m, dc): - # draw bezier curve - self.setColor(dc, 'blue') - # dc.DrawPointList([m.toClient(x, y) for x, y in Bezier(target_ctrl)]) - lst = list(BezierLine(target_ctrl)) - dc.DrawLineList([m.toClient(*p) + m.toClient(*q) for p, q in zip(lst, lst[1:])]) - - def drawguide(self, target_ctrl_start, target_ctrl_end, target_ctrl_x, target_ctrl_y, m, dc): - # - self.setColor(dc, 'black') - line = m.toClient(*target_ctrl_start) + m.toClient(*target_ctrl_end) - dc.DrawLine(*line) - - # draw control points - self.setColor(dc, 'red') - pnt = BezierPoint(*m.toClient(target_ctrl_x.GetValue(), target_ctrl_y.GetValue()) + (target_ctrl_x, target_ctrl_y)) - self.panel.addObj(pnt) - pnt.Draw(dc, True) - - def drawgrid(self, target_ctrl, m, dc): - xs, ys = m.ll - xe, ye = m.ur - - self.setColor(dc, 'black') - dc.DrawLine(*m.toClient(xs, 0) + m.toClient(xe, 0)) - dc.DrawLine(*m.toClient(0, ys) + m.toClient(0, ye)) - - dc.SetFont(self.panel.GetFont()) - dc.DrawText('%+d' % xs, *m.toClient(xs, 0)) - dc.DrawText('%+d' % ye, *m.toClient(0, ye)) - - def drawbeziererror(self, m, dc): - if self.panel.iserror: - xs, ys = m.ll - xe, ye = m.ur - - self.setColor(dc, 'red', 5) - dc.DrawLine(*m.toClient(0, 0) + m.toClient(xe, ye)) - dc.DrawLine(*m.toClient(0, ye) + m.toClient(xe, 0)) - - def setColor(self, dc, color, w=1): - dc.SetPen(wx.Pen(color, w)) - dc.SetBrush(wx.Brush(color)) - - def OnPaintBezierMouseLeftDown(self, event): - """マウスの左ボタンが押された時の処理""" - pos = event.GetPosition() # マウス座標を取得 - obj = self.findBezierPoint(pos) # マウス座標と重なってるオブジェクトを取得 - if obj is not None: - self.panel.drag_obj = obj # ドラッグ移動するオブジェクトを記憶 - self.panel.drag_start_pos = pos # ドラッグ開始時のマウス座標を記録 - self.panel.drag_obj.SavePosDiff(pos) - - def OnPaintBezierMouseLeftUp(self, event): - """マウスの左ボタンが離された時の処理""" - if self.panel.drag_obj is not None: - pos = event.GetPosition() - self.panel.drag_obj.pos.x = pos.x + self.panel.drag_obj.diff_pos.x - self.panel.drag_obj.pos.y = pos.y + self.panel.drag_obj.diff_pos.y - self.panel.drag_obj.UpdatePos() - self.parent.splitBezier() # ベジェ分割 - - self.panel.drag_obj = None - self.frame.Refresh(False) - - def OnPaintBezierMouseMotion(self, event): - """マウスカーソルが動いた時の処理""" - if self.panel.drag_obj is None: - # ドラッグしてるオブジェクトが無いなら処理しない - return - - # ドラッグしてるオブジェクトの座標値をマウス座標で更新 - pos = event.GetPosition() - self.panel.drag_obj.pos.x = pos.x + self.panel.drag_obj.diff_pos.x - self.panel.drag_obj.pos.y = pos.y + self.panel.drag_obj.diff_pos.y - self.panel.drag_obj.UpdatePos() - self.parent.splitBezier() # ベジェ分割 - self.frame.Refresh(False) # 描画更新 - - def findBezierPoint(self, pnt): - """マウス座標と重なってるオブジェクトを返す""" - result = None - for obj in self.panel.objs: - if obj.HitTest(pnt): - result = obj - return result - - -def pt(p, q, t): - assert 0 <= t <= 1 - return [a + (b - a) * float(t) for a, b in zip(p, q)] - - -def mid(p, q): - return pt(p, q, .5) - - -class BezierLine: - def __init__(self, ctrls, dt=3 * 1e-3): - self.ctrls = ctrls - self.dt = dt - - def __iter__(self): - ctrl = self.ctrls - dt = self.dt - - t = 0 - while t <= 1: - x, y = self.walkdown(ctrl, t) - yield x, y - t += dt - - def walkdown(self, ctrl, t): - dt = self.dt # noqa - if len(ctrl) == 1: - return ctrl[0] - else: - ps = [pt(p, q, t) for p, q in zip(ctrl, ctrl[1:])] - return self.walkdown(ps, t) - - -class Mapper(object): - # coordinate mapper: bounds -> client - def __init__(self, size, lowerleft, upperright): - self.size = size - self.ll = lowerleft - self.ur = upperright - - def toClient(self, x, y): - w, h = self.size - xs, ys = map(float, self.ll) - xe, ye = map(float, self.ur) - - xx, yy = x - xs, y - ys - xp, yp = (xe - xs) / w, (ye - ys) / h - xn, yn = xx / xp, h - yy / yp - # print x,y,'=>',xn,yn - return int(xn), int(yn) - - -class BezierPoint(): - - """マウスドラッグで移動できるオブジェクト用のクラス""" - - def __init__(self, x, y, target_ctrl_x, target_ctrl_y): - """コンストラクタ""" - self.size = 2 - self.pos = wx.Point(x, y) # 表示位置を記録 - self.diff_pos = wx.Point(0, 0) - self.target_ctrl_x = target_ctrl_x - self.target_ctrl_y = target_ctrl_y - - def HitTest(self, pnt): - """与えられた座標とアタリ判定して結果を返す""" - rect = self.GetRect() # 矩形領域を取得 - - # 座標が矩形内に入ってるか調べる - return rect.x - 5 <= pnt.x < (rect.x + rect.width + 5) and rect.y - 5 <= pnt.y < (rect.y + rect.height + 5) - - def GetRect(self): - """矩形領域を返す""" - return wx.Rect(self.pos.x, self.pos.y, self.size, self.size) - - def SavePosDiff(self, pnt): - """ - マウス座標と自分の座標の相対値を記録。 - この情報がないと、画像をドラッグした時の表示位置がしっくりこない - """ - self.diff_pos.x = self.pos.x - pnt.x - self.diff_pos.y = self.pos.y - pnt.y - - def Draw(self, dc, select_enable): - r = self.GetRect() # 矩形領域を取得 - - if select_enable: - # 丸を描画 - dc.DrawCircle(r.x, r.y, self.size) - - def UpdatePos(self): - self.target_ctrl_x.SetValue(self.pos.x) - self.target_ctrl_y.SetValue(127 - self.pos.y) - - -class BezierViewPanel(wx.Panel): - - def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.TAB_TRAVERSAL, name=wx.PanelNameStr): - panel = super(BezierViewPanel, self) - panel.__init__(parent, id=id, pos=pos, size=size, style=style, name=name) - self.objs = [] - self.drag_obj = None - self.drag_start_pos = (0, 0) - self.iserror = False - - def addObj(self, obj): - self.objs.append(obj) - diff --git a/src/form/panel/BlendPanel.py b/src/form/panel/BlendPanel.py deleted file mode 100644 index 13741b6..0000000 --- a/src/form/panel/BlendPanel.py +++ /dev/null @@ -1,299 +0,0 @@ -# -*- coding: utf-8 -*- -# -import wx -import wx.lib.newevent -import sys -import re - -from form.panel.BasePanel import BasePanel -from form.parts.BaseFilePickerCtrl import BaseFilePickerCtrl -from form.parts.ConsoleCtrl import ConsoleCtrl -from form.worker.BlendWorkerThread import BlendWorkerThread -from module.MMath import MRect, MVector3D, MVector4D, MQuaternion, MMatrix4x4 # noqa -from utils import MFormUtils, MFileUtils # noqa -from utils.MLogger import MLogger # noqa - -logger = MLogger(__name__) - -# イベント定義 -(BlendThreadEvent, EVT_CSV_THREAD) = wx.lib.newevent.NewEvent() - - -class BlendPanel(BasePanel): - - def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int): - super().__init__(frame, parent, tab_idx) - self.blend_worker = None - - self.description_txt = wx.StaticText(self, wx.ID_ANY, "指定されたPMXファイルのモーフをランダムに変化させた結果を、VMDファイルとして出力します。\n" \ - + "モーフの組み合わせが多くなると破綻する確率が高くなりますので、その状態での一般公開は避けてください。", - wx.DefaultPosition, wx.DefaultSize, 0) - self.sizer.Add(self.description_txt, 0, wx.ALL, 5) - - self.static_line = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) - self.sizer.Add(self.static_line, 0, wx.EXPAND | wx.ALL, 5) - - # PMXファイルコントロール - self.pmx_file_ctrl = LoadFilePickerCtrl(frame, self, u"PMXファイル", u"PMXファイルを開く", ("pmx"), wx.FLP_DEFAULT_STYLE, \ - u"モーフをブレンドさせたいPMXのパスを指定してください。\nD&Dでの指定、開くボタンからの指定ができます。\nパスを指定すると下部欄にモーフリストが表示されます。", \ - is_aster=False, is_save=False, set_no=0) - self.pmx_file_ctrl.file_ctrl.Bind(wx.EVT_FILEPICKER_CHANGED, self.on_change_file) - self.sizer.Add(self.pmx_file_ctrl.sizer, 0, wx.EXPAND, 0) - - # モーフ選択欄 --------- - self.morph_sizer = wx.BoxSizer(wx.HORIZONTAL) - - MORPH_TOOLTIP = "ブレンド対象となるモーフを選択して下さい。" - - self.morph_eye_txt = wx.StaticText(self, wx.ID_ANY, "目") - self.morph_eye_txt.SetToolTip(MORPH_TOOLTIP) - self.morph_sizer.Add(self.morph_eye_txt, 0, wx.EXPAND | wx.ALL, 5) - - self.morph_eye_list = wx.ListBox(self, id=wx.ID_ANY, pos=wx.DefaultPosition, size=(110, 100), choices=[], style=wx.LB_MULTIPLE | wx.LB_ALWAYS_SB) - self.morph_eye_list.SetToolTip(MORPH_TOOLTIP) - self.morph_sizer.Add(self.morph_eye_list, 0, wx.ALL, 5) - - self.morph_eyebrow_txt = wx.StaticText(self, wx.ID_ANY, "眉") - self.morph_eyebrow_txt.SetToolTip(MORPH_TOOLTIP) - self.morph_sizer.Add(self.morph_eyebrow_txt, 0, wx.EXPAND | wx.ALL, 5) - - self.morph_eyebrow_list = wx.ListBox(self, id=wx.ID_ANY, pos=wx.DefaultPosition, size=(110, 100), choices=[], style=wx.LB_MULTIPLE | wx.LB_ALWAYS_SB) - self.morph_eyebrow_list.SetToolTip(MORPH_TOOLTIP) - self.morph_sizer.Add(self.morph_eyebrow_list, 0, wx.ALL, 5) - - self.morph_lip_txt = wx.StaticText(self, wx.ID_ANY, "口") - self.morph_lip_txt.SetToolTip(MORPH_TOOLTIP) - self.morph_sizer.Add(self.morph_lip_txt, 0, wx.EXPAND | wx.ALL, 5) - - self.morph_lip_list = wx.ListBox(self, id=wx.ID_ANY, pos=wx.DefaultPosition, size=(110, 100), choices=[], style=wx.LB_MULTIPLE | wx.LB_ALWAYS_SB) - self.morph_lip_list.SetToolTip(MORPH_TOOLTIP) - self.morph_sizer.Add(self.morph_lip_list, 0, wx.ALL, 5) - - self.morph_other_txt = wx.StaticText(self, wx.ID_ANY, "他") - self.morph_other_txt.SetToolTip(MORPH_TOOLTIP) - self.morph_sizer.Add(self.morph_other_txt, 0, wx.EXPAND | wx.ALL, 5) - - self.morph_other_list = wx.ListBox(self, id=wx.ID_ANY, pos=wx.DefaultPosition, size=(110, 100), choices=[], style=wx.LB_MULTIPLE | wx.LB_ALWAYS_SB) - self.morph_other_list.SetToolTip(MORPH_TOOLTIP) - self.morph_sizer.Add(self.morph_other_list, 0, wx.ALL, 5) - - self.sizer.Add(self.morph_sizer, 0, wx.EXPAND | wx.ALL, 0) - - # モーフ増減値 --------- - - self.value_sizer = wx.BoxSizer(wx.HORIZONTAL) - - # モーフ最小値 - self.morph_value_min_txt = wx.StaticText(self, wx.ID_ANY, u"最小値", wx.DefaultPosition, wx.DefaultSize, 0) - self.morph_value_min_txt.SetToolTip(u"モーフ増減の最小値です。-10から10の間で設定できます。(小数点可)") - self.value_sizer.Add(self.morph_value_min_txt, 0, wx.EXPAND | wx.ALL, 5) - - self.morph_spin_min = wx.SpinCtrlDouble(self, id=wx.ID_ANY, size=wx.Size(80, -1), min=-10, max=10, initial=0.0, inc=0.1) - self.morph_spin_min.SetToolTip(u"モーフ増減の最小値です。-10から10の間で設定できます。(小数点可)") - self.morph_spin_min.Bind(wx.EVT_MOUSEWHEEL, lambda event: self.frame.on_wheel_spin_ctrl(event, 0.1)) - self.value_sizer.Add(self.morph_spin_min, 0, wx.ALL, 5) - - # モーフ最大値 - self.morph_value_max_txt = wx.StaticText(self, wx.ID_ANY, u"最大値", wx.DefaultPosition, wx.DefaultSize, 0) - self.morph_value_max_txt.SetToolTip(u"モーフ増減の最大値です。-10から10の間で設定できます。(小数点可)") - self.value_sizer.Add(self.morph_value_max_txt, 0, wx.EXPAND | wx.ALL, 5) - - self.morph_spin_max = wx.SpinCtrlDouble(self, id=wx.ID_ANY, size=wx.Size(80, -1), min=-10, max=10, initial=0.6, inc=0.1) - self.morph_spin_max.SetToolTip(u"モーフ増減の最大値です。-10から10の間で設定できます。(小数点可)") - self.morph_spin_max.Bind(wx.EVT_MOUSEWHEEL, lambda event: self.frame.on_wheel_spin_ctrl(event, 0.1)) - self.value_sizer.Add(self.morph_spin_max, 0, wx.ALL, 5) - - # モーフ増加値 - self.morph_value_inc_txt = wx.StaticText(self, wx.ID_ANY, u"増加値", wx.DefaultPosition, wx.DefaultSize, 0) - self.morph_value_inc_txt.SetToolTip(u"モーフ増減の増加量です。この増加量分ごとにモーフ組み合わせを生成していきます。0から1の間で設定できます。(小数点可)") - self.morph_value_inc_txt.Wrap(-1) - self.value_sizer.Add(self.morph_value_inc_txt, 0, wx.EXPAND | wx.ALL, 5) - - self.morph_spin_inc = wx.SpinCtrlDouble(self, id=wx.ID_ANY, size=wx.Size(80, -1), min=0.01, max=1, initial=0.1, inc=0.05) - self.morph_spin_inc.SetToolTip(u"モーフ増減の増加量です。この増加量分ごとにモーフ組み合わせを生成していきます。0から1の間で設定できます。(小数点可)") - self.morph_spin_inc.Bind(wx.EVT_MOUSEWHEEL, lambda event: self.frame.on_wheel_spin_ctrl(event, 0.05)) - self.value_sizer.Add(self.morph_spin_inc, 0, wx.ALL, 5) - - self.sizer.Add(self.value_sizer, 0, wx.EXPAND | wx.ALL, 0) - - btn_sizer = wx.BoxSizer(wx.HORIZONTAL) - - # モーフブレンド実行ボタン - self.blend_btn_ctrl = wx.Button(self, wx.ID_ANY, u"モーフブレンド実行", wx.DefaultPosition, wx.Size(200, 50), 0) - self.blend_btn_ctrl.SetToolTip(u"モーフをブレンドしたVMDを生成します。") - self.blend_btn_ctrl.Bind(wx.EVT_BUTTON, self.on_convert_blend) - btn_sizer.Add(self.blend_btn_ctrl, 0, wx.ALL, 5) - - self.sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER | wx.SHAPED, 5) - - # コンソール - self.console_ctrl = ConsoleCtrl(self, self.frame.logging_level, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(-1, 420), \ - wx.TE_MULTILINE | wx.TE_READONLY | wx.BORDER_NONE | wx.HSCROLL | wx.VSCROLL | wx.WANTS_CHARS) - self.console_ctrl.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT)) - self.console_ctrl.Bind(wx.EVT_CHAR, lambda event: MFormUtils.on_select_all(event, self.console_ctrl)) - self.sizer.Add(self.console_ctrl, 1, wx.ALL | wx.EXPAND, 5) - - # ゲージ - self.gauge_ctrl = wx.Gauge(self, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.GA_HORIZONTAL) - self.gauge_ctrl.SetValue(0) - self.sizer.Add(self.gauge_ctrl, 0, wx.ALL | wx.EXPAND, 5) - - self.fit() - - # フレームに変換完了処理バインド - self.frame.Bind(EVT_CSV_THREAD, self.on_convert_blend_result) - - def on_change_file(self, event: wx.Event): - # フォーム無効化 - self.disable() - # 出力先をCSVパネルのコンソールに変更 - sys.stdout = self.console_ctrl - - # ファイルコントロール自身のパス確定処理 - # 先頭と末尾の改行は除去 - target_path = self.pmx_file_ctrl.file_ctrl.GetPath().strip() - logger.test("target_path strip: %s", target_path) - - # 先頭と末尾のダブルクォーテーションは除去 - target_path = re.sub(r'^\\+\"(\w)\\', r'\1:\\', target_path) - target_path = target_path.strip("\"") - logger.test("target_path strip: %s", target_path) - - # 再設定 - self.pmx_file_ctrl.file_ctrl.SetPath(target_path) - - # ファイル読み込み処理 - if self.pmx_file_ctrl.is_valid() and self.pmx_file_ctrl.load(is_check=False): - # モーフ展開処理 - morph_names = {"目": [], "眉": [], "口": [], "他": []} - - for mk, mv in self.pmx_file_ctrl.data.morphs.items(): - if mv.display: - morph_names[mv.get_panel_name()].append(mk) - - self.morph_eye_list.SetItems(morph_names["目"]) - self.morph_eyebrow_list.SetItems(morph_names["眉"]) - self.morph_lip_list.SetItems(morph_names["口"]) - self.morph_other_list.SetItems(morph_names["他"]) - - # フォーム有効化 - self.enable() - - event.Skip() - - # フォーム無効化 - def disable(self): - self.pmx_file_ctrl.disable() - self.morph_eye_list.Disable() - self.morph_eyebrow_list.Disable() - self.morph_lip_list.Disable() - self.morph_other_list.Disable() - self.morph_spin_min.Disable() - self.morph_spin_max.Disable() - self.morph_spin_inc.Disable() - self.blend_btn_ctrl.Disable() - - # フォーム無効化 - def enable(self): - self.pmx_file_ctrl.enable() - self.morph_eye_list.Enable() - self.morph_eyebrow_list.Enable() - self.morph_lip_list.Enable() - self.morph_other_list.Enable() - self.morph_spin_min.Enable() - self.morph_spin_max.Enable() - self.morph_spin_inc.Enable() - self.blend_btn_ctrl.Enable() - - # モーフブレンド - def on_convert_blend(self, event: wx.Event): - # フォーム無効化 - self.disable() - # タブ固定 - self.fix_tab() - # コンソールクリア - self.console_ctrl.Clear() - # 出力先をブレンドパネルのコンソールに変更 - sys.stdout = self.console_ctrl - - wx.GetApp().Yield() - - self.elapsed_time = 0 - result = True - result = self.pmx_file_ctrl.is_valid() and result - result = self.is_valid() and result - - if not result: - # 終了音 - self.frame.sound_finish() - # タブ移動可 - self.release_tab() - # フォーム有効化 - self.enable() - # 出力先をデフォルトに戻す - if sys.stdout != self.frame.file_panel_ctrl.console_ctrl: - sys.stdout = self.frame.file_panel_ctrl.console_ctrl - - return result - - # モーフブレンド開始 - if self.blend_worker: - logger.error("まだ処理が実行中です。終了してから再度実行してください。", decoration=MLogger.DECORATION_BOX) - else: - # 別スレッドで実行 - self.blend_worker = BlendWorkerThread(self.frame, BlendThreadEvent) - self.blend_worker.start() - - return result - - event.Skip() - - # モーフブレンド完了処理 - def on_convert_blend_result(self, event: wx.Event): - self.elapsed_time = event.elapsed_time - - # 終了音 - self.frame.sound_finish() - - # タブ移動可 - self.release_tab() - # フォーム有効化 - self.enable() - # ワーカー終了 - self.blend_worker = None - # プログレス非表示 - self.gauge_ctrl.SetValue(0) - - if not event.result: - logger.error("モーフブレンド処理に失敗しました。", decoration=MLogger.DECORATION_BOX) - - event.Skip() - return False - - logger.info("モーフブレンドが完了しました", decoration=MLogger.DECORATION_BOX, title="OK") - - # 出力先をデフォルトに戻す - if sys.stdout != self.frame.file_panel_ctrl.console_ctrl: - sys.stdout = self.frame.file_panel_ctrl.console_ctrl - - def is_valid(self): - if len(self.morph_eye_list.GetSelections()) + len(self.morph_eyebrow_list.GetSelections()) \ - + len(self.morph_lip_list.GetSelections()) + len(self.morph_other_list.GetSelections()) == 0: - logger.warning("ブレント対象となるモーフが選択されていません", decoration=MLogger.DECORATION_BOX) - - return False - - return True - - -class LoadFilePickerCtrl(BaseFilePickerCtrl): - def __init__(self, frame, parent, title, message, file_type, style, tooltip, file_model_spacer=0, \ - title_parts_ctrl=None, file_parts_ctrl=None, title_parts2_ctrl=None, is_change_output=False, is_aster=False, is_save=False, set_no=0, required=True): - super().__init__(frame, parent, title, message, file_type, style, tooltip, file_model_spacer=0, title_parts_ctrl=None, file_parts_ctrl=None, title_parts2_ctrl=None, \ - is_change_output=False, is_aster=False, is_save=False, set_no=0, required=True) - - def on_change_file(self, event): - super().on_change_file(event) - - self.parent.on_change_file(event) - diff --git a/src/form/panel/CameraPanel.py b/src/form/panel/CameraPanel.py index 022871c..1fc57d2 100644 --- a/src/form/panel/CameraPanel.py +++ b/src/form/panel/CameraPanel.py @@ -46,11 +46,20 @@ def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int): self.camera_length_sizer = wx.BoxSizer(wx.HORIZONTAL) self.camera_length_txt = wx.StaticText(self.header_panel, wx.ID_ANY, u"距離調整可動範囲", wx.DefaultPosition, wx.DefaultSize, 0) - self.camera_length_txt.SetToolTip(u"カメラの距離の調整範囲を限定したい場合、指定して下さい。\n例えば「2」の場合、オリジナルカメラの「1/2」倍から「2」倍の範囲内でのみ距離を調整します。" \ - + "\nデフォルトの「5」だと、可動範囲無制限で「モデルの映り具合が同じになるように」最大限調整します。") + self.camera_length_txt.SetToolTip(u"ステージの大きさなどにより、カメラの距離の調整範囲を限定したい場合に\n" \ + + "カメラの距離可動範囲を限定することができます。\n" \ + + "可動範囲は手動で調整する事も可能です。") self.camera_length_txt.Wrap(-1) self.camera_length_sizer.Add(self.camera_length_txt, 0, wx.ALL, 5) + self.camera_length_type_ctrl = wx.Choice(self.header_panel, id=wx.ID_ANY, choices=["近距離", "中距離", "距離制限なし"]) + self.camera_length_type_ctrl.SetSelection(2) + self.camera_length_type_ctrl.Bind(wx.EVT_CHOICE, self.on_camera_length_type) + self.camera_length_type_ctrl.SetToolTip(u"「近距離」 … 小さめのステージ用。距離可動範囲を厳しめに制限します。\n" \ + + "「中距離」 … 中くらいのステージ用。距離可動範囲を多少制限します。\n" \ + + "「距離制限なし」 … 距離可動範囲を無制限とし、元モデルと同じ映り具合になるよう、最大限調整します。") + self.camera_length_sizer.Add(self.camera_length_type_ctrl, 0, wx.ALL, 5) + self.camera_length_label = wx.StaticText(self.header_panel, wx.ID_ANY, u"(5)", wx.DefaultPosition, wx.DefaultSize, 0) self.camera_length_label.SetToolTip(u"現在指定されているカメラ距離の可動範囲です。") self.camera_length_label.Wrap(-1) @@ -82,6 +91,16 @@ def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int): self.sizer.Layout() self.fit() + def on_camera_length_type(self, event): + if self.camera_length_type_ctrl.GetSelection() == 0: + self.camera_length_slider.SetValue(1.05) + elif self.camera_length_type_ctrl.GetSelection() == 1: + self.camera_length_slider.SetValue(1.3) + else: + self.camera_length_slider.SetValue(5) + + self.set_output_vmd_path(event) + def set_output_vmd_path(self, event, is_force=False): # カメラ出力パスを強制的に変更する self.header_panel.set_output_vmd_path(event, True) diff --git a/src/form/panel/SmoothPanel.py b/src/form/panel/SmoothPanel.py deleted file mode 100644 index 4226437..0000000 --- a/src/form/panel/SmoothPanel.py +++ /dev/null @@ -1,370 +0,0 @@ -# -*- coding: utf-8 -*- -# -import os -import wx -import wx.dataview -import sys - -from form.panel.BasePanel import BasePanel -from form.parts.BaseFilePickerCtrl import BaseFilePickerCtrl -from form.parts.HistoryFilePickerCtrl import HistoryFilePickerCtrl -from form.parts.ConsoleCtrl import ConsoleCtrl -from form.worker.SmoothWorkerThread import SmoothWorkerThread -from utils import MFormUtils, MFileUtils -from utils.MLogger import MLogger # noqa - -logger = MLogger(__name__) - -# イベント定義 -(SmoothThreadEvent, EVT_SMOOTH_THREAD) = wx.lib.newevent.NewEvent() - - -class SmoothPanel(BasePanel): - - def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int): - super().__init__(frame, parent, tab_idx) - self.convert_smooth_worker = None - - self.header_sizer = wx.BoxSizer(wx.VERTICAL) - - self.description_txt = wx.StaticText(self, wx.ID_ANY, u"指定されたVMDファイルに対して、キーを分割し、滑らかな補間曲線で繋いで、再出力します。\n" \ - + "スムージング回数1回で、全打ちとなり、2回目以降はフィルタリングした後に補間曲線で繋ぎます。", wx.DefaultPosition, wx.DefaultSize, 0) - self.header_sizer.Add(self.description_txt, 0, wx.ALL, 5) - - self.static_line01 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) - self.header_sizer.Add(self.static_line01, 0, wx.EXPAND | wx.ALL, 5) - - # 対象VMDファイルコントロール - self.smooth_vmd_file_ctrl = HistoryFilePickerCtrl(self.frame, self, u"対象モーションVMD", u"対象モーションVMDファイルを開く", ("vmd"), wx.FLP_DEFAULT_STYLE, \ - u"調整したい対象モーションのVMDパスを指定してください。\nD&Dでの指定、開くボタンからの指定、履歴からの選択ができます。", \ - file_model_spacer=46, title_parts_ctrl=None, title_parts2_ctrl=None, file_histories_key="smooth_vmd", is_change_output=True, \ - is_aster=False, is_save=False, set_no=1) - self.header_sizer.Add(self.smooth_vmd_file_ctrl.sizer, 1, wx.EXPAND, 0) - - # 対象PMXファイルコントロール - self.smooth_model_file_ctrl = HistoryFilePickerCtrl(self.frame, self, u"適用モデルPMX", u"適用モデルPMXファイルを開く", ("pmx"), wx.FLP_DEFAULT_STYLE, \ - u"モーションを適用したいモデルのPMXパスを指定してください。\n人体モデル以外にも適用可能です。\nD&Dでの指定、開くボタンからの指定、履歴からの選択ができます。", \ - file_model_spacer=52, title_parts_ctrl=None, title_parts2_ctrl=None, file_histories_key="smooth_pmx", \ - is_change_output=True, is_aster=False, is_save=False, set_no=1) - self.header_sizer.Add(self.smooth_model_file_ctrl.sizer, 1, wx.EXPAND, 0) - - # 出力先VMDファイルコントロール - self.output_smooth_vmd_file_ctrl = BaseFilePickerCtrl(frame, self, u"出力対象VMD", u"出力対象VMDファイルを開く", ("vmd"), wx.FLP_OVERWRITE_PROMPT | wx.FLP_SAVE | wx.FLP_USE_TEXTCTRL, \ - u"調整結果の対象VMD出力パスを指定してください。\n対象VMDファイル名に基づいて自動生成されますが、任意のパスに変更することも可能です。", \ - is_aster=False, is_save=True, set_no=1) - self.header_sizer.Add(self.output_smooth_vmd_file_ctrl.sizer, 1, wx.EXPAND, 0) - - self.sizer.Add(self.header_sizer, 0, wx.EXPAND | wx.ALL, 5) - - self.target_sizer = wx.BoxSizer(wx.HORIZONTAL) - - # ボーン名指定 - self.bone_target_txt_ctrl = wx.TextCtrl(self, wx.ID_ANY, "", wx.DefaultPosition, (450, 50), wx.HSCROLL | wx.VSCROLL | wx.TE_MULTILINE | wx.TE_READONLY) - self.bone_target_txt_ctrl.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT)) - self.target_sizer.Add(self.bone_target_txt_ctrl, 1, wx.EXPAND | wx.ALL, 5) - - self.bone_target_btn_ctrl = wx.Button(self, wx.ID_ANY, u"ボーン指定", wx.DefaultPosition, wx.DefaultSize, 0) - self.bone_target_btn_ctrl.SetToolTip(u"モーションに登録されているボーンから、スムージングにかけたいボーンを指定できます") - self.bone_target_btn_ctrl.Bind(wx.EVT_BUTTON, self.on_click_bone_target) - self.target_sizer.Add(self.bone_target_btn_ctrl, 0, wx.ALIGN_BOTTOM | wx.ALL, 5) - - self.sizer.Add(self.target_sizer, 0, wx.EXPAND | wx.ALL, 5) - - self.setting_sizer = wx.BoxSizer(wx.HORIZONTAL) - - # 処理回数 - self.loop_cnt_txt = wx.StaticText(self, wx.ID_ANY, u"処理回数", wx.DefaultPosition, wx.DefaultSize, 0) - self.setting_sizer.Add(self.loop_cnt_txt, 0, wx.ALL, 5) - - self.loop_cnt_ctrl = wx.SpinCtrl(self, id=wx.ID_ANY, size=wx.Size(60, -1), value="2", min=1, max=99999999, initial=2) - self.loop_cnt_ctrl.SetToolTip(u"スムージングを行う回数を指定してください。\n1回だと全打ちになります。\n2回目以降はフィルタをかけた上で間引きします。\n回数が増えると、変化が遅く、弱くなります。") - self.loop_cnt_ctrl.Bind(wx.EVT_SPINCTRL, self.on_change_file) - self.setting_sizer.Add(self.loop_cnt_ctrl, 0, wx.ALL, 5) - - # 補間 - self.interpolation_txt = wx.StaticText(self, wx.ID_ANY, u"補間方法", wx.DefaultPosition, wx.DefaultSize, 0) - self.setting_sizer.Add(self.interpolation_txt, 0, wx.ALL, 5) - - self.interpolation_ctrl = wx.Choice(self, id=wx.ID_ANY, choices=["補間曲線に従う", "補間曲線無視(円形)", "補間曲線無視(曲線)"]) - self.interpolation_ctrl.SetSelection(0) - self.interpolation_ctrl.SetToolTip(u"キーとキーの補間方法を指定してください。\n「補間曲線に従う」は、補間曲線に従って繋ぎます。" \ - + "\n「補間曲線無視(円形)」は、補間曲線を無視して、\n3つのキーを円周上に置いた円になるように補間します。" \ - + "\n「補間曲線無視(曲線)」は、補間曲線を無視して、\nキーを滑らかな曲線(カトマル曲線)で繋いで補間します。") - self.interpolation_ctrl.Bind(wx.EVT_CHOICE, self.on_change_file) - self.setting_sizer.Add(self.interpolation_ctrl, 0, wx.ALL, 5) - - self.sizer.Add(self.setting_sizer, 0, wx.EXPAND | wx.ALL, 5) - - btn_sizer = wx.BoxSizer(wx.HORIZONTAL) - - # スムージング変換実行ボタン - self.smooth_btn_ctrl = wx.Button(self, wx.ID_ANY, u"スムージング実行", wx.DefaultPosition, wx.Size(200, 50), 0) - self.smooth_btn_ctrl.SetToolTip(u"VMDを滑らかに繋いだモーションを再生成します。") - self.smooth_btn_ctrl.Bind(wx.EVT_BUTTON, self.on_convert_smooth) - btn_sizer.Add(self.smooth_btn_ctrl, 0, wx.ALL, 5) - - self.sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER | wx.SHAPED, 5) - - # コンソール - self.console_ctrl = ConsoleCtrl(self, self.frame.logging_level, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(-1, 420), \ - wx.TE_MULTILINE | wx.TE_READONLY | wx.BORDER_NONE | wx.HSCROLL | wx.VSCROLL | wx.WANTS_CHARS) - self.console_ctrl.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT)) - self.console_ctrl.Bind(wx.EVT_CHAR, lambda event: MFormUtils.on_select_all(event, self.console_ctrl)) - self.sizer.Add(self.console_ctrl, 1, wx.ALL | wx.EXPAND, 5) - - # ゲージ - self.gauge_ctrl = wx.Gauge(self, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.GA_HORIZONTAL) - self.gauge_ctrl.SetValue(0) - self.sizer.Add(self.gauge_ctrl, 0, wx.ALL | wx.EXPAND, 5) - - self.Layout() - self.fit() - - # ボーン選択用ダイアログ - self.bone_dialog = TargetBoneDialog(self.frame, self) - self.bone_list = [] - - # フレームに変換完了処理バインド - self.frame.Bind(EVT_SMOOTH_THREAD, self.on_convert_smooth_result) - - # ファイル変更時の処理 - def on_change_file(self, event: wx.Event): - self.set_output_vmd_path(event) - - def set_output_vmd_path(self, event, is_force=False): - output_smooth_vmd_path = MFileUtils.get_output_smooth_vmd_path( - self.smooth_vmd_file_ctrl.file_ctrl.GetPath(), - self.smooth_model_file_ctrl.file_ctrl.GetPath(), - self.output_smooth_vmd_file_ctrl.file_ctrl.GetPath(), - self.interpolation_ctrl.GetSelection(), - self.loop_cnt_ctrl.GetValue(), is_force) - - self.output_smooth_vmd_file_ctrl.file_ctrl.SetPath(output_smooth_vmd_path) - - if len(output_smooth_vmd_path) >= 255 and os.name == "nt": - logger.error("生成予定のファイルパスがWindowsの制限を超えています。\n生成予定パス: {0}".format(output_smooth_vmd_path), decoration=MLogger.DECORATION_BOX) - - # フォーム無効化 - def disable(self): - self.smooth_vmd_file_ctrl.disable() - self.smooth_model_file_ctrl.disable() - self.output_smooth_vmd_file_ctrl.disable() - self.loop_cnt_ctrl.Disable() - self.interpolation_ctrl.Disable() - self.smooth_btn_ctrl.Disable() - - # フォーム無効化 - def enable(self): - self.smooth_vmd_file_ctrl.enable() - self.smooth_model_file_ctrl.enable() - self.output_smooth_vmd_file_ctrl.enable() - self.loop_cnt_ctrl.Enable() - self.interpolation_ctrl.Enable() - self.smooth_btn_ctrl.Enable() - - # スムージング変換 - def on_convert_smooth(self, event: wx.Event): - # フォーム無効化 - self.disable() - # タブ固定 - self.fix_tab() - # コンソールクリア - self.console_ctrl.Clear() - # 出力先をスムージングパネルのコンソールに変更 - sys.stdout = self.console_ctrl - - wx.GetApp().Yield() - - self.smooth_vmd_file_ctrl.save() - self.smooth_model_file_ctrl.save() - - # JSON出力 - MFileUtils.save_history(self.frame.mydir_path, self.frame.file_hitories) - - self.elapsed_time = 0 - result = True - result = self.smooth_vmd_file_ctrl.is_valid() and self.smooth_model_file_ctrl.is_valid() and result - - if not result: - # 終了音 - self.frame.sound_finish() - # タブ移動可 - self.release_tab() - # フォーム有効化 - self.enable() - # 出力先をデフォルトに戻す - if sys.stdout != self.frame.file_panel_ctrl.console_ctrl: - sys.stdout = self.frame.file_panel_ctrl.console_ctrl - - return result - - # スムージング変換開始 - if self.convert_smooth_worker: - logger.error("まだ処理が実行中です。終了してから再度実行してください。", decoration=MLogger.DECORATION_BOX) - else: - # 別スレッドで実行 - self.convert_smooth_worker = SmoothWorkerThread(self.frame, SmoothThreadEvent, self.frame.is_saving) - self.convert_smooth_worker.start() - - return result - - # スムージング変換完了処理 - def on_convert_smooth_result(self, event: wx.Event): - self.elapsed_time = event.elapsed_time - logger.info("\n処理時間: %s", self.show_worked_time()) - - # 終了音 - self.frame.sound_finish() - - # タブ移動可 - self.release_tab() - # フォーム有効化 - self.enable() - # ワーカー終了 - self.convert_smooth_worker = None - # プログレス非表示 - self.gauge_ctrl.SetValue(0) - - # 出力先をデフォルトに戻す - if sys.stdout != self.frame.file_panel_ctrl.console_ctrl: - sys.stdout = self.frame.file_panel_ctrl.console_ctrl - - def show_worked_time(self): - # 経過秒数を時分秒に変換 - td_m, td_s = divmod(self.elapsed_time, 60) - - if td_m == 0: - worked_time = "{0:02d}秒".format(int(td_s)) - else: - worked_time = "{0:02d}分{1:02d}秒".format(int(td_m), int(td_s)) - - return worked_time - - def on_click_bone_target(self, event: wx.Event): - self.disable() - - sys.stdout = self.console_ctrl - # VMD読み込み - self.smooth_vmd_file_ctrl.load() - # PMX読み込み - self.smooth_model_file_ctrl.load() - - if (self.smooth_vmd_file_ctrl.data and self.smooth_model_file_ctrl.data and \ - (self.smooth_vmd_file_ctrl.data.digest != self.bone_dialog.vmd_digest or self.smooth_model_file_ctrl.data.digest != self.bone_dialog.pmx_digest)): - - # データが揃ってたら押下可能 - self.bone_target_btn_ctrl.Enable() - # リストクリア - self.bone_target_txt_ctrl.SetValue("") - # ボーン選択用ダイアログ - self.bone_dialog.Destroy() - self.bone_dialog = TargetBoneDialog(self.frame, self) - else: - if not self.smooth_vmd_file_ctrl.data or not self.smooth_model_file_ctrl.data: - logger.error("対象モーションVMDもしくは適用モデルPMXが未指定です。", decoration=MLogger.DECORATION_BOX) - self.enable() - return - - self.enable() - - if self.bone_dialog.ShowModal() == wx.ID_CANCEL: - return # the user changed their mind - - # 選択されたボーンリストを入力欄に設定 - self.bone_list = self.bone_dialog.get_bone_list() - self.bone_target_txt_ctrl.SetValue(', '.join(self.bone_list)) - - self.bone_dialog.Hide() - - def get_bone_list(self): - return self.bone_list - - -class TargetBoneDialog(wx.Dialog): - - def __init__(self, frame: wx.Frame, panel: wx.Panel): - super().__init__(frame, id=wx.ID_ANY, title="対象ボーン指定", pos=(-1, -1), size=(700, 450), style=wx.DEFAULT_DIALOG_STYLE, name="TargetBoneDialog") - - self.frame = frame - self.panel = panel - self.vmd_digest = 0 if not self.panel.smooth_vmd_file_ctrl.data else self.panel.smooth_vmd_file_ctrl.data.digest - self.pmx_digest = 0 if not self.panel.smooth_model_file_ctrl.data else self.panel.smooth_model_file_ctrl.data.digest - self.org_bones = [""] # 選択肢文言 - self.rep_bones = [""] - self.org_choices = [] # 選択コントロール - self.rep_mx_choices = [] - self.rep_my_choices = [] - self.rep_mz_choices = [] - self.rep_rx_choices = [] - self.rep_ry_choices = [] - self.rep_rz_choices = [] - - self.sizer = wx.BoxSizer(wx.VERTICAL) - - # 説明文 - self.description_txt = wx.StaticText(self, wx.ID_ANY, u"スムージングしたいボーン名を選択してください。\nCtrlキーを押しながら選択すると、複数ボーンを選択できます。Shiftキーで一括選択できます。", wx.DefaultPosition, wx.DefaultSize, 0) - self.sizer.Add(self.description_txt, 0, wx.ALL, 5) - - # ボタン - self.btn_sizer = wx.BoxSizer(wx.HORIZONTAL) - self.ok_btn = wx.Button(self, wx.ID_OK, "OK") - self.btn_sizer.Add(self.ok_btn, 0, wx.ALL, 5) - - self.calcel_btn = wx.Button(self, wx.ID_CANCEL, "キャンセル") - self.btn_sizer.Add(self.calcel_btn, 0, wx.ALL, 5) - - self.sizer.Add(self.btn_sizer, 0, wx.ALIGN_RIGHT | wx.ALL, 5) - - self.static_line01 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) - self.sizer.Add(self.static_line01, 0, wx.EXPAND | wx.ALL, 5) - - # データツリー - self.tree_ctrl = wx.TreeCtrl(self, id=wx.ID_ANY, pos=(-1, -1), size=(650, 400), style=wx.TR_ROW_LINES | wx.TR_MULTIPLE) - - if self.panel.smooth_model_file_ctrl.data and self.panel.smooth_vmd_file_ctrl.data: - model = self.panel.smooth_model_file_ctrl.data - motion = self.panel.smooth_vmd_file_ctrl.data - - tr_root_ctrl = self.tree_ctrl.AddRoot(text=model.name) - - for display_name, display_slot in model.display_slots.items(): - bone_list = [] - for (display_type, bone_idx) in display_slot.references: - if display_type == 0 and bone_idx in model.bone_indexes and model.bone_indexes[bone_idx] in motion.bones.keys(): - # 表示枠にボーンがあって、モーションにもある場合、追加 - bone_list.append(model.bone_indexes[bone_idx]) - - if len(bone_list) > 0: - # 追加対象がある場合、ツリー追加 - - display_ctrl = self.tree_ctrl.AppendItem(parent=tr_root_ctrl, text=display_name) - - for bone_name in bone_list: - self.tree_ctrl.AppendItem(parent=display_ctrl, text=bone_name) - - self.tree_ctrl.Expand(display_ctrl) - - self.tree_ctrl.Expand(tr_root_ctrl) - - self.sizer.Add(self.tree_ctrl, 0, wx.ALL, 5) - - self.SetSizer(self.sizer) - self.sizer.Layout() - - # 画面中央に表示 - self.CentreOnScreen() - - # 最初は隠しておく - self.Hide() - - def get_bone_list(self): - bone_list = [] - - for bone_ctrl_id in self.tree_ctrl.GetSelections(): - bone_list.append(self.tree_ctrl.GetItemText(bone_ctrl_id)) - - return bone_list - - - - diff --git a/src/form/parts/FloatSliderCtrl.py b/src/form/parts/FloatSliderCtrl.py index d0e5bf8..60bc4e0 100644 --- a/src/form/parts/FloatSliderCtrl.py +++ b/src/form/parts/FloatSliderCtrl.py @@ -61,6 +61,9 @@ def SetValue(self, value): self._islider.SetValue(round(value / self._res)) self._value = value + # logger.debug('OnScroll: value=%f, ival=%d', self._value, ival) + self._label.SetLabel(u"({0})".format(round(self._value, 3))) + def SetMin(self, minval): self._islider.SetMin(round(minval / self._res)) self._min = minval diff --git a/src/form/worker/BlendWorkerThread.py b/src/form/worker/BlendWorkerThread.py deleted file mode 100644 index 3b166da..0000000 --- a/src/form/worker/BlendWorkerThread.py +++ /dev/null @@ -1,56 +0,0 @@ -# -*- coding: utf-8 -*- -# - -import gc -import wx -import time -from form.worker.BaseWorkerThread import BaseWorkerThread, task_takes_time -from service.MorphBlendService import MorphBlendService -from module.MOptions import MBlendOptions - - -class BlendWorkerThread(BaseWorkerThread): - - def __init__(self, frame: wx.Frame, result_event: wx.Event): - self.elapsed_time = 0 - self.frame = frame - self.result_event = result_event - self.gauge_ctrl = frame.blend_panel_ctrl.gauge_ctrl - self.options = None - - super().__init__(frame, self.result_event, frame.blend_panel_ctrl.console_ctrl) - - @task_takes_time - def thread_event(self): - start = time.time() - - self.result = self.frame.blend_panel_ctrl.pmx_file_ctrl.load() and self.result - - if self.result: - eye_list = [self.frame.blend_panel_ctrl.morph_eye_list.GetString(idx) for idx in self.frame.blend_panel_ctrl.morph_eye_list.GetSelections()] - eyebrow_list = [self.frame.blend_panel_ctrl.morph_eyebrow_list.GetString(idx) for idx in self.frame.blend_panel_ctrl.morph_eyebrow_list.GetSelections()] - lip_list = [self.frame.blend_panel_ctrl.morph_lip_list.GetString(idx) for idx in self.frame.blend_panel_ctrl.morph_lip_list.GetSelections()] - other_list = [self.frame.blend_panel_ctrl.morph_other_list.GetString(idx) for idx in self.frame.blend_panel_ctrl.morph_other_list.GetSelections()] - - self.options = MBlendOptions(\ - version_name=self.frame.version_name, \ - logging_level=self.frame.logging_level, \ - model=self.frame.blend_panel_ctrl.pmx_file_ctrl.data, \ - eye_list=eye_list, \ - eyebrow_list=eyebrow_list, \ - lip_list=lip_list, \ - other_list=other_list, \ - min_value=self.frame.blend_panel_ctrl.morph_spin_min.GetValue(), \ - max_value=self.frame.blend_panel_ctrl.morph_spin_max.GetValue(), \ - inc_value=self.frame.blend_panel_ctrl.morph_spin_inc.GetValue()) - - self.result = MorphBlendService(self.options).execute() and self.result - - self.elapsed_time = time.time() - start - - def thread_delete(self): - del self.options - gc.collect() - - def post_event(self): - wx.PostEvent(self.frame, self.result_event(result=self.result, elapsed_time=self.elapsed_time)) diff --git a/src/form/worker/SmoothWorkerThread.py b/src/form/worker/SmoothWorkerThread.py deleted file mode 100644 index 038b2c6..0000000 --- a/src/form/worker/SmoothWorkerThread.py +++ /dev/null @@ -1,59 +0,0 @@ -# -*- coding: utf-8 -*- -# - -import os -import wx -import time -import gc -from form.worker.BaseWorkerThread import BaseWorkerThread, task_takes_time -from service.ConvertSmoothService import ConvertSmoothService -from module.MOptions import MSmoothOptions -from utils.MLogger import MLogger # noqa - -logger = MLogger(__name__) - - -class SmoothWorkerThread(BaseWorkerThread): - - def __init__(self, frame: wx.Frame, result_event: wx.Event, is_exec_saving: bool): - self.elapsed_time = 0 - self.frame = frame - self.result_event = result_event - self.gauge_ctrl = frame.smooth_panel_ctrl.gauge_ctrl - self.is_exec_saving = is_exec_saving - self.options = None - - super().__init__(frame, self.result_event, frame.smooth_panel_ctrl.console_ctrl) - - @task_takes_time - def thread_event(self): - start = time.time() - - self.result = self.frame.smooth_panel_ctrl.smooth_vmd_file_ctrl.load() and self.result - self.result = self.frame.smooth_panel_ctrl.smooth_model_file_ctrl.load(is_check=False) and self.result - - if self.result: - self.options = MSmoothOptions(\ - version_name=self.frame.version_name, \ - logging_level=self.frame.logging_level, \ - motion=self.frame.smooth_panel_ctrl.smooth_vmd_file_ctrl.data, \ - model=self.frame.smooth_panel_ctrl.smooth_model_file_ctrl.data, \ - output_path=self.frame.smooth_panel_ctrl.output_smooth_vmd_file_ctrl.file_ctrl.GetPath(), \ - loop_cnt=self.frame.smooth_panel_ctrl.loop_cnt_ctrl.GetValue(), \ - interpolation=self.frame.smooth_panel_ctrl.interpolation_ctrl.GetSelection(), \ - bone_list=self.frame.smooth_panel_ctrl.get_bone_list(), \ - monitor=self.frame.smooth_panel_ctrl.console_ctrl, \ - is_file=False, \ - outout_datetime=logger.outout_datetime, \ - max_workers=(1 if self.is_exec_saving else min(32, os.cpu_count() + 4))) - - self.result = ConvertSmoothService(self.options).execute() and self.result - - self.elapsed_time = time.time() - start - - def thread_delete(self): - del self.options - gc.collect() - - def post_event(self): - wx.PostEvent(self.frame, self.result_event(result=self.result, elapsed_time=self.elapsed_time)) diff --git a/src/module/MOptions.pxd b/src/module/MOptions.pxd index 06537c8..21f7c3f 100644 --- a/src/module/MOptions.pxd +++ b/src/module/MOptions.pxd @@ -79,21 +79,3 @@ cdef class MArmProcessOptions(): cdef public double alignment_distance_floor cdef public bint arm_check_skip_flg - -cdef class MSmoothOptions(): - cdef public str version_name - cdef public int logging_level - cdef public int max_workers - cdef public VmdMotion motion - cdef public PmxModel model - cdef public str output_path - cdef public int loop_cnt - cdef public int interpolation - cdef public list bone_list - cdef public object monitor - cdef public bint is_file - cdef public str outout_datetime - -cdef c_smooth_parse(str version_name) - - diff --git a/src/module/MOptions.pyx b/src/module/MOptions.pyx index 4a8b223..b9d74e5 100644 --- a/src/module/MOptions.pyx +++ b/src/module/MOptions.pyx @@ -324,84 +324,3 @@ class MVmdOptions(): self.morph_csv_path = morph_csv_path self.camera_csv_path = camera_csv_path - -class MBlendOptions(): - - def __init__(self, version_name, logging_level, model, eye_list, eyebrow_list, lip_list, other_list, min_value, max_value, inc_value): - self.version_name = version_name - self.logging_level = logging_level - self.model = model - self.eye_list = eye_list - self.eyebrow_list = eyebrow_list - self.lip_list = lip_list - self.other_list = other_list - self.min_value = min_value - self.max_value = max_value - self.inc_value = inc_value - - -cdef class MSmoothOptions(): - - def __init__(self, version_name, logging_level, max_workers, motion, model, output_path, loop_cnt, interpolation, bone_list, monitor, is_file, outout_datetime): - self.version_name = version_name - self.logging_level = logging_level - self.motion = motion - self.model = model - self.output_path = output_path - self.loop_cnt = loop_cnt - self.interpolation = interpolation - self.bone_list = bone_list - self.monitor = monitor - self.is_file = is_file - self.outout_datetime = outout_datetime - self.max_workers = max_workers - - @classmethod - def parse(cls, version_name: str): - return c_smooth_parse(version_name) - - -cdef c_smooth_parse(str version_name): - parser = argparse.ArgumentParser() - parser.add_argument('--motion_path', dest='motion_path', help='input vmd', type=str) - parser.add_argument('--model_path', dest='model_path', help='model_path', type=str) - parser.add_argument('--loop_cnt', dest='loop_cnt', help='loop_cnt', type=int) - parser.add_argument('--interpolation', dest='interpolation', help='interpolation', type=int) - parser.add_argument("--bone_list", default=[], type=(lambda x: list(map(str, x.split(';'))))) - parser.add_argument("--verbose", type=int, default=20) - - args = parser.parse_args() - - # ログディレクトリ作成 - os.makedirs("log", exist_ok=True) - - MLogger.initialize(level=args.verbose, is_file=True) - - try: - motion = VmdReader(args.motion_path).read_data() - model = PmxReader(args.model_path).read_data() - - # 出力ファイルパス - output_vmd_path = MFileUtils.get_output_smooth_vmd_path(motion.path, model.path, "", args.interpolation, args.loop_cnt, True) - - options = MSmoothOptions(\ - version_name=version_name, \ - logging_level=args.verbose, \ - motion=motion, \ - model=model, \ - output_path=output_vmd_path, \ - loop_cnt=args.loop_cnt, \ - interpolation=args.interpolation, \ - bone_list=args.bone_list, \ - monitor=sys.stdout, \ - is_file=True, \ - outout_datetime=logger.outout_datetime, \ - max_workers=1) - - return options - except SizingException as se: - logger.error("スムージング処理が処理できないデータで終了しました。\n\n%s", se.message, decoration=MLogger.DECORATION_BOX) - except Exception as e: - logger.critical("スムージング処理が意図せぬエラーで終了しました。", e, decoration=MLogger.DECORATION_BOX) - - diff --git a/src/service/ConvertSmoothService.py b/src/service/ConvertSmoothService.py deleted file mode 100644 index d088b07..0000000 --- a/src/service/ConvertSmoothService.py +++ /dev/null @@ -1,600 +0,0 @@ -# -*- coding: utf-8 -*- -# -import math -import numpy as np -import logging -import os -import traceback -import concurrent.futures -from concurrent.futures import ThreadPoolExecutor - -from module.MOptions import MSmoothOptions, MOptionsDataSet -from mmd.PmxData import PmxModel # noqa -from mmd.VmdData import VmdMotion, VmdBoneFrame, VmdCameraFrame, VmdInfoIk, VmdLightFrame, VmdMorphFrame, VmdShadowFrame, VmdShowIkFrame # noqa -from mmd.VmdWriter import VmdWriter -import module.MMath as MMath -from module.MMath import MRect, MVector3D, MVector4D, MQuaternion, MMatrix4x4 # noqa -from utils import MUtils, MServiceUtils, MBezierUtils # noqa -from utils.MLogger import MLogger # noqa -from utils.MException import SizingException - -logger = MLogger(__name__, level=1) - - -class ConvertSmoothService(): - def __init__(self, options: MSmoothOptions): - self.options = options - - def execute(self): - logging.basicConfig(level=self.options.logging_level, format="%(message)s [%(module_name)s]") - - try: - service_data_txt = "スムージング処理実行\n------------------------\nexeバージョン: {version_name}\n".format(version_name=self.options.version_name) \ - - service_data_txt = "{service_data_txt} VMD: {vmd}\n".format(service_data_txt=service_data_txt, - vmd=os.path.basename(self.options.motion.path)) # noqa - service_data_txt = "{service_data_txt} モデル: {model}({model_name})\n".format(service_data_txt=service_data_txt, - model=os.path.basename(self.options.motion.path), model_name=self.options.model.name) # noqa - service_data_txt = "{service_data_txt} 対象ボーン: {bone_names}回\n".format(service_data_txt=service_data_txt, - bone_names=", ".join(self.options.bone_list)) # noqa - service_data_txt = "{service_data_txt} 処理回数: {loop_cnt}回\n".format(service_data_txt=service_data_txt, - loop_cnt=self.options.loop_cnt) # noqa - service_data_txt = "{service_data_txt} 補間方法: {interpolation}\n".format(service_data_txt=service_data_txt, - interpolation=("補間曲線に従う" if self.options.interpolation == 0 else "補間曲線無視(円形)" if self.options.interpolation == 1 else "補間曲線無視(曲線)")) # noqa - - logger.info(service_data_txt, decoration=MLogger.DECORATION_BOX) - - # 処理に成功しているか - result = self.convert_smooth() - - # 最後に出力 - VmdWriter(MOptionsDataSet(self.options.motion, None, self.options.model, self.options.output_path, False, False, [], None, 0, [])).write() - - logger.info("出力終了: %s", os.path.basename(self.options.output_path), decoration=MLogger.DECORATION_BOX, title="成功") - - return result - except SizingException as se: - logger.error("スムージング処理が処理できないデータで終了しました。\n\n%s", se.message, decoration=MLogger.DECORATION_BOX) - except Exception: - logger.critical("スムージング処理が意図せぬエラーで終了しました。\n\n%s", traceback.format_exc(), decoration=MLogger.DECORATION_BOX) - finally: - logging.shutdown() - - # スムージング処理実行 - def convert_smooth(self): - # 最初に全打ち - - futures = [] - with ThreadPoolExecutor(thread_name_prefix="prepare", max_workers=self.options.max_workers) as executor: - for bone_name in self.options.motion.bones.keys(): - if bone_name in self.options.model.bones and bone_name in self.options.bone_list: - if self.options.interpolation == 0 and len(self.options.motion.bones[bone_name].keys()) >= 2: - # 線形補間の場合、そのまま全打ち - futures.append(executor.submit(self.prepare_linear, bone_name)) - elif self.options.interpolation == 1: - if len(self.options.motion.bones[bone_name].keys()) > 2: - # 円形補間の場合、円形全打ち - futures.append(executor.submit(self.prepare_circle, bone_name)) - else: - # 円形補間でキー数が足りない場合、線形補間 - logger.warning("円形補間が指定されましたが、キー数が3つに満たないため、計算出来ません。ボーン名: %s", bone_name) - futures.append(executor.submit(self.prepare_linear, bone_name)) - elif self.options.interpolation == 2: - if len(self.options.motion.bones[bone_name].keys()) > 2: - # 曲線補間の場合、カトマル曲線全打ち - futures.append(executor.submit(self.prepare_curve, bone_name)) - else: - # 曲線補間でキー数が足りない場合、線形補間 - logger.warning("曲線補間が指定されましたが、キー数が3つに満たないため、計算出来ません。ボーン名: %s", bone_name) - futures.append(executor.submit(self.prepare_linear, bone_name)) - concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) - - for f in futures: - if not f.result(): - return False - - # 処理回数が2回以上の場合、不要キー削除 - if self.options.loop_cnt >= 2: - futures = [] - with ThreadPoolExecutor(thread_name_prefix="remove", max_workers=self.options.max_workers) as executor: - for bone_name in self.options.motion.bones.keys(): - if bone_name in self.options.model.bones and bone_name in self.options.bone_list: - futures.append(executor.submit(self.remove_filterd_bf, bone_name)) - concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) - - for f in futures: - if not f.result(): - return False - - return True - - # 不要キー削除処理 - def remove_filterd_bf(self, bone_name: str): - try: - logger.copy(self.options) - - for n in range(1, self.options.loop_cnt): - # 処理回数が3回以上の場合、フィルタをかける - if self.options.loop_cnt > 2 and n > 2: - logger.info("【フィルタリング%s回目】%s 開始", n - 1, bone_name) - self.options.motion.smooth_filter_bf(0, bone_name, self.options.model.bones[bone_name].getRotatable(), \ - self.options.model.bones[bone_name].getTranslatable(), \ - config={"freq": 30, "mincutoff": 1, "beta": 1, "dcutoff": 1}) - logger.info("【フィルタリング%s回目】%s 終了", n - 1, bone_name) - - logger.info("【不要キー削除%s回目】%s 開始", n, bone_name) - self.options.motion.remove_unnecessary_bf(0, bone_name, self.options.model.bones[bone_name].getRotatable(), \ - self.options.model.bones[bone_name].getTranslatable(), offset=0) - logger.info("【不要キー削除%s回目】%s 終了", n, bone_name) - - return True - except SizingException as se: - logger.error("スムージング処理が処理できないデータで終了しました。\n\n%s", se.message) - return False - except Exception as e: - logger.error("スムージング処理が意図せぬエラーで終了しました。", e) - return False - - # 線形補間で全打ち - def prepare_linear(self, bone_name: str): - try: - logger.copy(self.options) - - logger.info("【スムージング1回目】%s 開始", bone_name) - - # 各ボーンのbfを全打ち(スムージングが複数回数の場合、キー自体は無効のまま) - self.options.motion.regist_full_bf(1, [bone_name], offset=1, is_key=(self.options.loop_cnt <= 1)) - - logger.info("【スムージング1回目】%s 終了", bone_name) - - return True - except SizingException as se: - logger.error("スムージング処理が処理できないデータで終了しました。\n\n%s", se.message) - return False - except Exception as e: - logger.error("スムージング処理が意図せぬエラーで終了しました。", e) - return False - - # 曲線補間で全打ち - def prepare_curve(self, bone_name: str): - try: - logger.copy(self.options) - - logger.info("【スムージング1回目】%s 開始", bone_name) - - # 全キーフレを取得 - fnos = self.options.motion.get_bone_fnos(bone_name, is_read=True) - - rx_values = [] - ry_values = [] - rz_values = [] - mx_values = [] - my_values = [] - mz_values = [] - - for fno in fnos: - bf = self.options.motion.calc_bf(bone_name, fno) - - if self.options.model.bones[bone_name].getRotatable(): - euler = bf.rotation.toEulerAngles() - rx_values.append(euler.x()) - ry_values.append(euler.y()) - rz_values.append(euler.z()) - - if self.options.model.bones[bone_name].getTranslatable(): - mx_values.append(bf.position.x()) - my_values.append(bf.position.y()) - mz_values.append(bf.position.z()) - - if self.options.model.bones[bone_name].getRotatable(): - rx_all_values = MBezierUtils.calc_value_from_catmullrom(bone_name, fnos, rx_values) - logger.info("【スムージング1回目】%s - 回転X 終了", bone_name) - - ry_all_values = MBezierUtils.calc_value_from_catmullrom(bone_name, fnos, ry_values) - logger.info("【スムージング1回目】%s - 回転Y 終了", bone_name) - - rz_all_values = MBezierUtils.calc_value_from_catmullrom(bone_name, fnos, rz_values) - logger.info("【スムージング1回目】%s - 回転Z 終了", bone_name) - else: - if len(fnos) > 0: - rx_all_values = np.zeros(fnos[-1] + 1) - ry_all_values = np.zeros(fnos[-1] + 1) - rz_all_values = np.zeros(fnos[-1] + 1) - else: - rx_all_values = [0] - ry_all_values = [0] - rz_all_values = [0] - - if self.options.model.bones[bone_name].getTranslatable(): - mx_all_values = MBezierUtils.calc_value_from_catmullrom(bone_name, fnos, mx_values) - logger.info("【スムージング1回目】%s - 移動X 終了", bone_name) - - my_all_values = MBezierUtils.calc_value_from_catmullrom(bone_name, fnos, my_values) - logger.info("【スムージング1回目】%s - 移動Y 終了", bone_name) - - mz_all_values = MBezierUtils.calc_value_from_catmullrom(bone_name, fnos, mz_values) - logger.info("【スムージング1回目】%s - 移動Z 終了", bone_name) - else: - if len(fnos) > 0: - mx_all_values = np.zeros(fnos[-1] + 1) - my_all_values = np.zeros(fnos[-1] + 1) - mz_all_values = np.zeros(fnos[-1] + 1) - else: - mx_all_values = [0] - my_all_values = [0] - mz_all_values = [0] - - # カトマル曲線で生成した値を全打ち - for fno, (rx, ry, rz, mx, my, mz) in enumerate(zip(rx_all_values, ry_all_values, rz_all_values, mx_all_values, my_all_values, mz_all_values)): - bf = self.options.motion.calc_bf(bone_name, fno) - bf.rotation = MQuaternion.fromEulerAngles(rx, ry, rz) - bf.position = MVector3D(mx, my, mz) - self.options.motion.regist_bf(bf, bone_name, fno) - - logger.info("【スムージング1回目】%s 終了", bone_name) - - return True - except SizingException as se: - logger.error("スムージング処理が処理できないデータで終了しました。\n\n%s", se.message) - return False - except Exception as e: - logger.error("スムージング処理が意図せぬエラーで終了しました。", e) - return False - - # 円形補間で全打ち - def prepare_circle(self, bone_name: str): - try: - logger.copy(self.options) - - logger.info("【スムージング1回目】%s 開始", bone_name) - - if self.options.model.bones[bone_name].fixed_axis != MVector3D(): - # 回転は3Dベクトルに直すため、そのボーンのローカル軸の向きを先に計算しておく - local_axis = calc_local_axis(self.options.model, bone_name) - else: - # 通常ボーンはそのまま回す - local_axis = MVector3D() - - prev_sep_fno = 0 - fnos = self.options.motion.get_bone_fnos(bone_name, is_read=True) - for prev_fno, now_fno, next_fno in zip(fnos[:-2], fnos[1:-1], fnos[2:]): - - prev_bf = self.options.motion.calc_bf(bone_name, prev_fno) - now_bf = self.options.motion.calc_bf(bone_name, now_fno) - next_bf = self.options.motion.calc_bf(bone_name, next_fno) - - # 読み込み時のキーで3キーフレで伸ばす - for fno in range(prev_fno, next_fno): - if fno in fnos: - # 読み込みキーはスルー - continue - - target_bf = VmdBoneFrame(fno) - target_bf.set_name(bone_name) - target_bf.key = True - - if self.options.model.bones[bone_name].getRotatable(): - # 回転可ボーンの場合、回転円形補間 - self.interpolate_rot_circle(prev_bf, now_bf, next_bf, target_bf, local_axis) - - if self.options.model.bones[bone_name].getTranslatable(): - # 移動可ボーンの場合、移動円形補間 - self.interpolate_mov_circle(prev_bf, now_bf, next_bf, target_bf) - - self.options.motion.regist_bf(target_bf, bone_name, fno) - - if fno // 500 > prev_sep_fno and fnos[-1] > 0: - logger.info("-- %sフレーム目:終了(%s%) %s", fno, round((fno / fnos[-1]) * 100, 3), bone_name) - prev_sep_fno = fno // 500 - - logger.info("【スムージング1回目】%s 終了", bone_name) - - return True - except SizingException as se: - logger.error("スムージング処理が処理できないデータで終了しました。\n\n%s", se.message) - return False - except Exception as e: - logger.error("スムージング処理が意図せぬエラーで終了しました。", e) - return False - - # 移動の円形補間 - def interpolate_mov_circle(self, prev_bf: VmdBoneFrame, now_bf: VmdBoneFrame, next_bf: VmdBoneFrame, target_bf: VmdBoneFrame): - p = prev_bf.position.copy() - w = now_bf.position.copy() - n = next_bf.position.copy() - - if target_bf.fno < now_bf.fno: - # nowより前の場合 - # 変化量 - t = (target_bf.fno - prev_bf.fno) / (now_bf.fno - prev_bf.fno) - - # デフォルト値 - d = w + (w - p) - - out = interpolate_vec3(p, w, n, d, t, target_bf.fno) - else: - # nowより後の場合 - # 変化量 - t = (target_bf.fno - now_bf.fno) / (next_bf.fno - now_bf.fno) - - # デフォルト値 - d = n + (n - w) - - out = interpolate_vec3(w, n, p, d, t, target_bf.fno) - - target_bf.position = out - - # 移動の円形補間 - def interpolate_rot_circle(self, prev_bf: VmdBoneFrame, now_bf: VmdBoneFrame, next_bf: VmdBoneFrame, target_bf: VmdBoneFrame, local_axis: MVector3D): - if local_axis != MVector3D(): - # 軸がある場合、その方向に回す - p_qq = MQuaternion.fromAxisAndAngle(local_axis, prev_bf.rotation.toDegree()) - w_qq = MQuaternion.fromAxisAndAngle(local_axis, now_bf.rotation.toDegree()) - n_qq = MQuaternion.fromAxisAndAngle(local_axis, next_bf.rotation.toDegree()) - - p = p_qq.toEulerAngles() - w = w_qq.toEulerAngles() - n = n_qq.toEulerAngles() - else: - p_qq = prev_bf.rotation.copy() - w_qq = now_bf.rotation.copy() - n_qq = next_bf.rotation.copy() - - # 軸がない場合、そのまま回転 - p = p_qq.toEulerAngles() - w = w_qq.toEulerAngles() - n = n_qq.toEulerAngles() - - if target_bf.fno < now_bf.fno: - # 変化量 - t = (target_bf.fno - prev_bf.fno) / (now_bf.fno - prev_bf.fno) - - # デフォルト値 - d_qq = MQuaternion.slerp(p_qq, w_qq, t) - d = d_qq.toEulerAngles() - - out = interpolate_vec3(p, w, n, d, t, target_bf.fno) - else: - # 変化量 - t = (target_bf.fno - now_bf.fno) / (next_bf.fno - now_bf.fno) - - # デフォルト値 - d_qq = MQuaternion.slerp(w_qq, n_qq, t) - d = d_qq.toEulerAngles() - - out = interpolate_vec3(w, n, p, d, t, target_bf.fno) - - out_qq = MQuaternion.fromEulerAngles(out.x(), out.y(), out.z()) - - if local_axis != MVector3D(): - # 回転を元に戻す - if target_bf.fno < now_bf.fno: - d2_qq = MQuaternion.slerp(prev_bf.rotation, now_bf.rotation, t) - else: - d2_qq = MQuaternion.slerp(now_bf.rotation, next_bf.rotation, t) - - result_qq = (d_qq.inverted() * out_qq * d2_qq) - else: - result_qq = out_qq - - target_bf.rotation = result_qq - - -# 指定されたボーンの最終的なローカル軸を求める -def calc_local_axis(model, bone_name): - # 定義されていないのも含め全ボーンリンクを取得する - links = model.create_link_2_top_one(bone_name, is_defined=False) - - # 初期スタンスのボーングローバル位置と行列を取得 - global_3ds_dic, total_mats = MServiceUtils.calc_global_pos(model, links, VmdMotion(), 0, return_matrix=True, is_local_x=True) - - # target_mat = MMatrix4x4() - # target_mat.setToIdentity() - - # # 処理対象行列 - # for n, (lname, mat) in enumerate(total_mats.items()): - # target_link = links.get(lname) - - # if n == 0: - # # 最初は行列そのもの - # target_mat = mat.copy() - # else: - # # 2番目以降は行列をかける - # target_mat *= mat.copy() - - # if n > 0: - # # ボーン自身にローカル軸が設定されているか - # local_x_matrix = MMatrix4x4() - # local_x_matrix.setToIdentity() - - # local_axis_qq = MQuaternion() - - # if target_link.local_x_vector == MVector3D(): - # # ローカル軸が設定されていない場合、計算 - - # # 自身から親を引いた軸の向き - # local_axis = target_link.position - links.get(lname, offset=-1).position - # local_axis_qq = MQuaternion.fromDirection(local_axis.normalized(), MVector3D(0, 0, 1)) - # else: - # # ローカル軸が設定されている場合、その値を採用 - # local_axis_qq = MQuaternion.fromDirection(target_link.local_x_vector.normalized(), MVector3D(0, 0, 1)) - - # local_x_matrix.rotate(local_axis_qq) - - # target_mat *= local_x_matrix - - # ワールド座標系から注目ノードの局所座標系への変換 - inv_coord = total_mats[bone_name].inverted() - - # 自身のボーンからの向き先を取得 - axis = model.get_local_x_axis(bone_name) - - # 最終的な対象ボーンのローカル軸の向き - local_axis = (inv_coord * axis).normalized() - - return local_axis - - -# vec3での滑らかな変化 -def interpolate_vec3(op: MVector3D, ow: MVector3D, on: MVector3D, d: MVector3D, t: float, f: int): - # 念のためコピー - p = op.copy() - w = ow.copy() - n = on.copy() - - # 半径は3点間の距離の最長の半分 - r = max(p.distanceToPoint(w), p.distanceToPoint(n), w.distanceToPoint(n)) / 2 - - logger.test("op: %s, ow: %s, on: %s, d: %s, t: %s, f: %s, r: %s", p.to_log(), w.to_log(), n.to_log(), d.to_log(), t, f, r) - - if r == 0: - # 半径が取れなかった場合、そもそもまったく移動がないので、線分移動 - return (p + n) * t - - if p == w or p == n or w == n: - # 半径が0の場合か、どれか同じ値の場合、線対称な値を使用する - n = d - - # 3点を通る球体の原点を求める - c, radius = calc_sphere_center(p, w, n, r) - - if radius == 0: - # 半径が取れなかった場合、そもそもまったく移動がないので、線分移動 - return (p + n) * t - - # prev -> now の t分の回転量 - pn_qq = MQuaternion.rotationTo((p - c).normalized(), (c - c).normalized()) - pw_qq = MQuaternion.rotationTo((p - c).normalized(), (w - c).normalized()) - # 球形補間の移動量 - t_qq = MQuaternion.slerp(pn_qq, pw_qq, t) - - logger.test("(p - c): %s, (c - c): %s, (w - c): %s", (p - c).normalized(), (c - c).normalized(), (w - c).normalized()) - logger.test("pn_qq: %s, pw: %s, t: %s", pn_qq, pw_qq, t_qq) - - out = t_qq * (p - c) + c - - # 値の変化がない場合、上書き - if p.x() == w.x() == n.x(): - out.setX(w.x()) - if p.y() == w.y() == n.y(): - out.setY(w.y()) - if p.z() == w.z() == n.z(): - out.setZ(w.z()) - - out.effective() - - logger.test(out.to_log()) - - return out - - -# 指定された3点と半径を通る球の中心点を求める -# https://oshiete.goo.ne.jp/qa/195295.html -# https://okwave.jp/qa/q9467739.html -def calc_sphere_center(pv, wv, nv, r): - # 大体0の場合は代替値で求める - x1 = pv.x() if not MMath.is_almost_null(pv.x()) else 0.00001 - y1 = pv.y() if not MMath.is_almost_null(pv.y()) else 0.00001 - z1 = pv.z() if not MMath.is_almost_null(pv.z()) else 0.00001 - x2 = wv.x() if not MMath.is_almost_null(wv.x()) else 0.00001 - y2 = wv.y() if not MMath.is_almost_null(wv.y()) else 0.00001 - z2 = wv.z() if not MMath.is_almost_null(wv.z()) else 0.00001 - x3 = nv.x() if not MMath.is_almost_null(nv.x()) else 0.00001 - y3 = nv.y() if not MMath.is_almost_null(nv.y()) else 0.00001 - z3 = nv.z() if not MMath.is_almost_null(nv.z()) else 0.00001 - - m = (pv + wv + nv) / 3 - - try: - tm01=x1**2-x2**2+y1**2-y2**2+z1**2-z2**2 # noqa - tm02=x1**2-x3**2+y1**2-y3**2+z1**2-z3**2 # noqa - tm11=-2*(x1-x2)*(z1-z3)+2*(x1-x3)*(z1-z2) # noqa - tm12=-2*(y1-y2)*(z1-z3)+2*(y1-y3)*(z1-z2) # noqa - tm13=tm01*(z1-z3)-tm02*(z1-z2) # noqa - tm21=-2*(x1-x2)*(y1-y3)+2*(x1-x3)*(y1-y2) # noqa - tm22=-2*(z1-z2)*(y1-y3)+2*(z1-z3)*(y1-y2) # noqa - tm23=tm01*(y1-y3)-tm02*(y1-y2) # noqa - tma=1+tm11**2/tm12**2+tm21**2/tm22**2 # noqa - tmb=-2*x1+2*(y1+tm13/tm12)*tm11/tm12+2*(z1+tm23/tm22)*tm21/tm22 # noqa - tmc=x1**2+(y1+tm13/tm12)**2+(z1+tm23/tm22)**2-r**2 # noqa - xq1=(-tmb+math.sqrt(abs(tmb**2-4*tma*tmc)))/2/tma # noqa - xq2=(-tmb-math.sqrt(abs(tmb**2-4*tma*tmc)))/2/tma # noqa - yq1=-tm13/tm12-tm11/tm12*xq1 # noqa - yq2=-tm13/tm12-tm11/tm12*xq2 # noqa - zq1=-tm23/tm22-tm21/tm22*xq1 # noqa - zq2=-tm23/tm22-tm21/tm22*xq2 # noqa - - c1 = MVector3D(xq1, yq1, zq1) - c2 = MVector3D(xq2, yq2, zq2) - - if c1.isnan() or c2.isnan(): - # 球体の原点が求められなかった場合、二次元円として求める - if round(x1, 1) == round(x2, 1) == round(x3, 1): - # 同じ値の場合、2次元円として求める - cx, cy, r = calc_circle_center(y1, z1, y2, z2, y3, z3) - return MVector3D(x1, cx, cy), r - - if round(y1, 1) == round(y2, 1) == round(y3, 1): - cx, cy, r = calc_circle_center(x1, z1, x2, z2, x3, z3) - return MVector3D(cx, y1, cy), r - - if round(z1, 1) == round(z2, 1) == round(z3, 1): - cx, cy, r = calc_circle_center(x1, y1, x2, y2, x3, y3) - return MVector3D(cx, cy, z1), r - - logger.test("c1: %s(%s), c2: %s(%s)", c1.to_log(), c1.isnan(), c2.to_log(), c2.isnan()) - - if c1 == c2: - # 重解 - return c1, r - - if c1.distanceToPoint(m) < c2.distanceToPoint(m): - # 3点の中間に近い方を返す - return c1, r - - return c2, r - except ZeroDivisionError: - - if round(x1, 1) == round(x2, 1) == round(x3, 1): - # 同じ値の場合、2次元円として求める - cx, cy, r = calc_circle_center(y1, z1, y2, z2, y3, z3) - return MVector3D(x1, cx, cy), r - - if round(y1, 1) == round(y2, 1) == round(y3, 1): - cx, cy, r = calc_circle_center(x1, z1, x2, z2, x3, z3) - return MVector3D(cx, y1, cy), r - - if round(z1, 1) == round(z2, 1) == round(z3, 1): - cx, cy, r = calc_circle_center(x1, y1, x2, y2, x3, y3) - return MVector3D(cx, cy, z1), r - - return MVector3D(), 0 - - -# http://www.iot-kyoto.com/satoh/2016/01/29/tangent-003/ -# http://nobutina.blog86.fc2.com/blog-entry-674.html -def calc_circle_center(x1, y1, x2, y2, x3, y3): - - G=( y2*x1-y1*x2 +y3*x2-y2*x3 +y1*x3-y3*x1 ) # noqa - - try: - Xc= ((x1*x1+y1*y1)*(y2-y3)+(x2*x2+y2*y2)*(y3-y1)+(x3*x3+y3*y3)*(y1-y2))/(2*G) # noqa - Yc=-((x1*x1+y1*y1)*(x2-x3)+(x2*x2+y2*y2)*(x3-x1)+(x3*x3+y3*y3)*(x1-x2))/(2*G) # noqa - - Xd=(((x1*x1+y1*y1)-(x2*x2+y2*y2))*(y2-y3)-((x2*x2+y2*y2)-(x3*x3+y3*y3))*(y1-y2))/(2*((x1-x2)*(y2-y3)-(x2-x3)*(y1-y2))) # noqa - Yd=(((y1*y1+x1*x1)-(y2*y2+x2*x2))*(x2-x3)-((y2*y2+x2*x2)-(y3*y3+x3*x3))*(x1-x2))/(2*((y1-y2)*(x2-x3)-(y2-y3)*(x1-x2))) # noqa - - G = 2 * math.sqrt((x1 - Xc) * (x1 - Xc) + (y1 - Yc) * (y1 - Yc)) - - return Xd, Yd, G / 2 - except ZeroDivisionError: - - if round(x1, 1) == round(x2, 1) == round(x3, 1): - G = math.sqrt((y1 + y2 + y3) ** 2) - return (x1 + x2 + x3) / 3, (y1 + y2 + y3) / 3, G - - G = math.sqrt((x1 + x2 + x3) ** 2) - return (x1 + x2 + x3) / 3, (y1 + y2 + y3) / 3, G - - return 0, 0, 0 - diff --git a/src/service/MorphBlendService.py b/src/service/MorphBlendService.py deleted file mode 100644 index 4e87c98..0000000 --- a/src/service/MorphBlendService.py +++ /dev/null @@ -1,159 +0,0 @@ -# -*- coding: utf-8 -*- -# -import copy -import math -import logging -import os -import traceback -import itertools -import random -from datetime import datetime - -from mmd.VmdWriter import VmdWriter -from mmd.VmdData import VmdMotion, VmdMorphFrame # noqa -from module.MOptions import MCsvOptions, MOptionsDataSet -from utils import MFileUtils -from utils.MException import SizingException -from utils.MLogger import MLogger # noqa - -logger = MLogger(__name__) - - -class MorphBlendService(): - def __init__(self, options: MCsvOptions): - self.options = options - - def execute(self): - logging.basicConfig(level=self.options.logging_level, format="%(message)s [%(module_name)s]") - - try: - service_data_txt = "モーフブレンド処理実行\n------------------------\nexeバージョン: {version_name}\n".format(version_name=self.options.version_name) - - service_data_txt = "{service_data_txt}モデル: {model_path} ({model_name})\n".format(service_data_txt=service_data_txt, - model_path=os.path.basename(self.options.model.path), model_name=self.options.model.name) # noqa - - service_data_txt = "{service_data_txt}  目: {target}\n".format(service_data_txt=service_data_txt, target=",".join(self.options.eye_list)) # noqa - service_data_txt = "{service_data_txt}  眉: {target}\n".format(service_data_txt=service_data_txt, target=",".join(self.options.eyebrow_list)) # noqa - service_data_txt = "{service_data_txt}  口: {target}\n".format(service_data_txt=service_data_txt, target=",".join(self.options.lip_list)) # noqa - service_data_txt = "{service_data_txt}  他: {target}\n".format(service_data_txt=service_data_txt, target=",".join(self.options.other_list)) # noqa - service_data_txt = "{service_data_txt} 範囲: {min}~{max} ({inc})".format(service_data_txt=service_data_txt, \ - min=self.options.min_value, max=self.options.max_value, inc=self.options.inc_value) # noqa - - logger.info(service_data_txt, decoration=MLogger.DECORATION_BOX) - - # 処理に成功しているか - result = self.blend_morph() - - return result - except SizingException as se: - logger.error("モーフブレンド処理が処理できないデータで終了しました。\n\n%s", se.message, decoration=MLogger.DECORATION_BOX) - except Exception: - logger.critical("モーフブレンド処理が意図せぬエラーで終了しました。\n\n%s", traceback.format_exc(), decoration=MLogger.DECORATION_BOX) - finally: - logging.shutdown() - - # モーフブレンド処理実行 - def blend_morph(self): - # モーションVMDディレクトリパス - pmx_dir_path = MFileUtils.get_dir_path(self.options.model.path) - # モーションVMDファイル名・拡張子 - pmx_file_name, pmx_ext = os.path.splitext(os.path.basename(self.options.model.path)) - - dt_now = datetime.now() - - blend_fpath = "{0}\\{1}_blend_{2:%Y%m%d_%H%M%S}.vmd".format(pmx_dir_path, pmx_file_name, dt_now) - - bone_motion = VmdMotion() - - # 処理対象モーフ名(文字列) - target_morphs = self.options.eye_list + self.options.eyebrow_list + self.options.lip_list + self.options.other_list - - # 処理対象モーフデータ - all_morphs = [] - for mk, mv in self.options.model.morphs.items(): - if mv.display and mv.name in target_morphs: - all_morphs.append(mv) - bone_motion.morphs[mk] = {} - - # 変化量(少ない方のの割合を多くする) - ratio_values = [self.options.inc_value * x for x in range(math.ceil(self.options.min_value / self.options.inc_value), \ - math.ceil(self.options.max_value / self.options.inc_value) + 1) if self.options.min_value <= self.options.inc_value * x <= self.options.max_value] - ratio_lower_values = [self.options.inc_value * x for x in range(math.ceil(self.options.min_value / self.options.inc_value), \ - math.ceil(self.options.max_value / self.options.inc_value / 2) + 1) if self.options.min_value <= self.options.inc_value * x <= self.options.max_value] - ratio_zero_values = [0 for x in range(len(all_morphs))] - - # 全体の比率増減量 - ratio_total_values = copy.deepcopy(ratio_values) - ratio_total_values.extend(ratio_lower_values) - ratio_total_values.extend(ratio_lower_values) - ratio_total_values.extend(ratio_zero_values) - - # モーフの組合せ数 - morph_comb_cnt = 1 - # 変化量の組合せ数 - ratio_product_cnt = 1 - for n in range(len(all_morphs), 0, -1): - morph_comb_cnt *= n - ratio_product_cnt *= len(ratio_values) - - morph_total_cnt = 0 - - if morph_comb_cnt * ratio_product_cnt <= 1000: - # モーフの組合せ - morph_comb = list(itertools.combinations(all_morphs, len(all_morphs))) - logger.debug("morph_comb: %s", len(morph_comb)) - - # 変化量の直積(同じ値を許容する) - ratio_product = list(itertools.product(ratio_values, repeat=len(all_morphs))) - logger.debug("ratio_product: %s", len(ratio_product)) - - # 組合せが1000以下なら組合せをそのまま出力 - brend_mr_pairs_list = list(itertools.product(morph_comb, ratio_product)) - logger.debug("brend_mr_pairs_list: %s", len(brend_mr_pairs_list)) - - for mframe, (morphs, ratios) in enumerate(brend_mr_pairs_list): - # 上限までしか登録しない - if morph_total_cnt > 19000: - break - - for morph, ratio in zip(morphs, ratios): - vmd_morph = VmdMorphFrame() - vmd_morph.fno = mframe - vmd_morph.set_name(morph.name) - vmd_morph.ratio = ratio - vmd_morph.key = True - - bone_motion.morphs[morph.name][mframe] = vmd_morph - logger.test(vmd_morph) - - morph_total_cnt += 1 - - else: - # 組合せが1000より多い場合、ランダム - for mframe in range(1000): - # 上限までしか登録しない - if morph_total_cnt > 19000: - break - - for morph in all_morphs: - ratio = ratio_total_values[random.randint(0, len(ratio_values) - 1)] - - vmd_morph = VmdMorphFrame() - vmd_morph.fno = mframe - vmd_morph.set_name(morph.name) - vmd_morph.ratio = ratio - vmd_morph.key = True - - bone_motion.morphs[morph.name][mframe] = vmd_morph - logger.test(vmd_morph) - - morph_total_cnt += 1 - - data_set = MOptionsDataSet(bone_motion, self.options.model, self.options.model, blend_fpath, False, False, [], None, 0, []) - - VmdWriter(data_set).write() - - logger.info("モーフブレンドVMD: %s", blend_fpath, decoration=MLogger.DECORATION_BOX) - - return True - diff --git a/src/service/parts/CameraService.py b/src/service/parts/CameraService.py index 10e7788..da3bb80 100644 --- a/src/service/parts/CameraService.py +++ b/src/service/parts/CameraService.py @@ -15,9 +15,9 @@ logger = MLogger(__name__, level=1) # 顔系ボーン名 -HEAD_BONE_NAMES = ["頭頂実体", "頭", "首", "左目", "右目"] +HEAD_BONE_NAMES = ["頭頂実体", "頭", "首", "左目", "右目", "首根元"] # 体幹ボーン名 -TRUNK_BONE_NAMES = ["上半身2", "上半身", "首根元"] +TRUNK_BONE_NAMES = ["上半身2", "上半身"] # 足底ボーン名 LEG_BOTTOM_BONE_NAMES = ["右足底実体", "左足底実体"] @@ -95,8 +95,9 @@ def execute_rep_camera(self, fno: int, cf: VmdCameraFrame, past_cf: VmdCameraFra # ------------------------ # 画面内に映ってるボーンINDEXの中央値を仮の中央座標とする - org_mean_vec = MVector3D(np.mean(np.array(list(org_inner_global_poses.values())), axis=0)) - rep_mean_vec = MVector3D(np.mean(np.array(list(rep_inner_global_poses.values())), axis=0)) + # 複数人モーションのカメラの場合を想定して、上下端は見ない + org_target_vec = MVector3D(np.mean(np.array(list(org_inner_global_poses.values())), axis=0)) + rep_target_vec = MVector3D(np.mean(np.array(list(rep_inner_global_poses.values())), axis=0)) # カメラ角度 camera_qq = self.calc_camera_qq(cf) @@ -112,19 +113,30 @@ def execute_rep_camera(self, fno: int, cf: VmdCameraFrame, past_cf: VmdCameraFra logger.test("camera_origin: %s", camera_origin) # ボーンの相対位置 - org_nearest_relative_vec = (camera_origin - org_mean_vec) - logger.debug("org_mean_vec: %s", org_mean_vec.to_log()) + org_nearest_relative_vec = (camera_origin - org_target_vec) + logger.debug("org_target_vec: %s", org_target_vec.to_log()) logger.debug("org_nearest_relative_vec: %s", org_nearest_relative_vec.to_log()) # 距離0の場合のカメラの位置を算出 - cf_pos = rep_mean_vec + (org_nearest_relative_vec * ratio) + cf_pos = rep_target_vec + (org_nearest_relative_vec * ratio) logger.debug("cf_pos: %s", cf_pos) + length_ratio = ratio + pos_ratio = ratio + + if camera_length < 5: + # カメラの距離を再設定 + # 可動範囲内に収める(2020/10/22) + length_ratio = min(camera_length, max(1 / camera_length, ratio)) + # 原点用比率は、距離が可動範囲内ならば比率そのまま、範囲外ならば体格比率まで + pos_ratio = ratio if length_ratio == ratio else \ + min(self.camera_options[nearest_data_set_idx].body_ratio, max(1 / self.camera_options[nearest_data_set_idx].body_ratio, ratio)) + mat_len = MMatrix4x4() mat_len.setToIdentity() mat_len.translate(cf_pos) mat_len.rotate(camera_qq) - mat_len.translate(MVector3D(0, 0, -cf.length * ratio)) + mat_len.translate(MVector3D(0, 0, -cf.length * pos_ratio)) # 距離を除いたカメラの原点に合わせる camera_length_origin = mat_len * MVector3D() logger.test("camera_length_origin: %s", camera_length_origin) @@ -132,13 +144,10 @@ def execute_rep_camera(self, fno: int, cf: VmdCameraFrame, past_cf: VmdCameraFra # 距離を除いたカメラの原点を再設定 cf.position = camera_length_origin - # カメラの距離を再設定 - # 可動範囲内に収める(2020/10/22) - limit_ratio = min(camera_length, max(1 / camera_length, ratio)) if camera_length < 5 else ratio - cf.length = cf.length * limit_ratio + cf.length = cf.length * length_ratio # 比率を保持 - cf.ratio = limit_ratio + cf.ratio = length_ratio offset_length = 0 offset_angle = 0 @@ -147,7 +156,8 @@ def execute_rep_camera(self, fno: int, cf: VmdCameraFrame, past_cf: VmdCameraFra rep_inner_square_poses = {} # この時点の距離と位置で変換先モデルの体幹+目の映り具合をチェック(上下が分かってないと大きさ取れない) - if top_data_set_idx >= 0 and top_bone_name and bottom_data_set_idx >= 0 and bottom_bone_name: + # 距離制限が掛かっている場合、スルー + if top_data_set_idx >= 0 and top_bone_name and bottom_data_set_idx >= 0 and bottom_bone_name and camera_length == 5: # 先モデル上下グローバル位置 rep_top_pos = rep_inner_global_poses[(top_data_set_idx, top_bone_name)] rep_bottom_pos = rep_inner_global_poses[(bottom_data_set_idx, bottom_bone_name)] @@ -185,8 +195,8 @@ def execute_rep_camera(self, fno: int, cf: VmdCameraFrame, past_cf: VmdCameraFra cnt += 1 - logger.info("%sフレーム目 縮尺比率: %s, 注視点: %s-%s, 上辺: %s-%s, 下辺: %s-%s, 距離オフセット: %s, 視野角オフセット: %s", \ - cf.fno, round(limit_ratio, 5), (nearest_data_set_idx + 1), nearest_bone_name.replace("実体", ""), \ + logger.info("%sフレーム目 原点比率: %s, 距離比率: %s, 注視点: %s-%s, 上辺: %s-%s, 下辺: %s-%s, 距離オフセット: %s, 視野角オフセット: %s", \ + cf.fno, round(pos_ratio, 5), round(length_ratio, 5), (nearest_data_set_idx + 1), nearest_bone_name.replace("実体", ""), \ (top_data_set_idx + 1), top_bone_name.replace("実体", ""), (bottom_data_set_idx + 1), bottom_bone_name.replace("実体", ""), \ round(offset_length, 5), offset_angle) @@ -205,14 +215,14 @@ def calc_camera_ratio(self, fno: int, cf: VmdCameraFrame): # まず全体のグローバル位置とプロジェクション座標正規位置を算出 self.calc_org_project_square_poses(fno, cf, 0, -1, all_org_global_poses, all_org_project_square_poses) # 画面内に映っているINDEXリスト - org_inner_global_poses, org_inner_square_poses = self.calc_inner_index(fno, all_org_global_poses, all_org_project_square_poses, None, 0, 0) + org_inner_global_poses, org_inner_square_poses = self.calc_inner_index(fno, all_org_global_poses, all_org_project_square_poses, None, 0, 0.1) logger.debug("f: %s, inner: %s", fno, org_inner_global_poses.keys()) # 画面内に映っている顔系INDEX(注視点直近算出のためなので、ちょっと範囲広め) org_face_inner_global_poses, org_face_inner_square_poses = self.calc_inner_index(fno, org_inner_global_poses, org_inner_square_poses, HEAD_BONE_NAMES, 0, 0.1) # 画面内に映っている体幹系INDEX org_trunk_inner_global_poses, org_trunk_inner_square_poses = self.calc_inner_index(fno, org_inner_global_poses, org_inner_square_poses, \ - HEAD_BONE_NAMES + TRUNK_BONE_NAMES + LEG_BOTTOM_BONE_NAMES, 0, 0.1) + TRUNK_BONE_NAMES + LEG_BOTTOM_BONE_NAMES, 0, 0.1) if len(org_face_inner_square_poses.keys()) > 0: # 顔系が画面内に映っている場合、その時点で注視点直近を取得する @@ -242,12 +252,8 @@ def calc_camera_ratio(self, fno: int, cf: VmdCameraFrame): logger.debug("f: %s, nearest d: %s, k: %s, v: %s, s: %s", fno, nearest_data_set_idx, nearest_bone_name, \ all_org_global_poses[(nearest_data_set_idx, nearest_bone_name)], all_org_project_square_poses[(nearest_data_set_idx, nearest_bone_name)]) - # 画面端ボーンを計算する(体幹が取れている場合、体幹ベースで画面端を取る。体幹ベースは少し離れていても対象とする) - if len(org_trunk_inner_square_poses.keys()) > 0: - (top_data_set_idx, top_bone_name), (bottom_data_set_idx, bottom_bone_name) = self.calc_top_botom_index(fno, cf, org_trunk_inner_square_poses, org_inner_global_poses, 0, 0.1) - else: - (top_data_set_idx, top_bone_name), (bottom_data_set_idx, bottom_bone_name) = self.calc_top_botom_index(fno, cf, org_inner_square_poses, org_inner_global_poses, 0, 0.1) - + # 画面端ボーンを計算する + (top_data_set_idx, top_bone_name), (bottom_data_set_idx, bottom_bone_name) = self.calc_top_botom_index(fno, cf, org_inner_square_poses, org_inner_global_poses, 0, 0.1) (left_data_set_idx, left_bone_name), (right_data_set_idx, right_bone_name) = self.calc_left_right_index(fno, cf, org_inner_square_poses, org_inner_global_poses, 0, 0.1) org_top_diff = rep_top_diff = org_bottom_diff = rep_bottom_diff = 0 @@ -372,7 +378,7 @@ def calc_top_botom_index(self, fno: int, cf: VmdCameraFrame, all_square_poses: d break logger.debug("f: %s, 画面 check: square_poses[yi][1]: %s, square_poses[yi][2]: %s, top_edge_pos[1]: %s, top_edge_pos[2]: %s", \ - fno, square_poses[yi][1], square_poses[yi][2], top_edge_pos[1], top_edge_pos[2]) + fno, square_poses[yi][1], square_poses[yi][2], top_edge_pos[1], top_edge_pos[2]) top_target_key = (int(square_keys[yi][0]), str(square_keys[yi][1])) top_target_global_pos = org_inner_global_poses[top_target_key] @@ -389,7 +395,7 @@ def calc_top_botom_index(self, fno: int, cf: VmdCameraFrame, all_square_poses: d break logger.debug("f: %s, 画面 check: square_poses[yi][1]: %s, square_poses[yi][2]: %s, top_edge_pos[1]: %s, top_edge_pos[2]: %s", \ - fno, square_poses[yi][1], square_poses[yi][2], bottom_edge_pos[1], bottom_edge_pos[2]) + fno, square_poses[yi][1], square_poses[yi][2], bottom_edge_pos[1], bottom_edge_pos[2]) bottom_target_key = (int(square_keys[yi][0]), str(square_keys[yi][1])) bottom_target_global_pos = org_inner_global_poses[bottom_target_key] @@ -688,6 +694,7 @@ def calc_ratio(self, data_set_idx: int, model: PmxModel, model_type: str): logger.info("【No.%s】%sモデルの頭頂頂点INDEX: %s (%s)", (data_set_idx + 1), model_type, model.head_top_vertex.index, \ model.head_top_vertex.position.to_log()) + face_length = 1 if "頭" in model.bones: # 顔の大きさ face_length = model.bones["頭頂実体"].position.y() - model.bones["頭"].position.y() @@ -706,7 +713,8 @@ def calc_ratio(self, data_set_idx: int, model: PmxModel, model_type: str): # 顔の大きさ / 全身の高さ で頭身算出 return total_height, face_length, total_height / face_length - def prepare_link(self, org_model: PmxModel, rep_model: PmxModel, camera_offset_y: float, org_links: list, org_link_target: dict, rep_links: dict, link_bone_name_list: list, target_bone_name_list: list): + def prepare_link(self, org_model: PmxModel, rep_model: PmxModel, camera_offset_y: float, org_links: list, org_link_target: dict, \ + rep_links: dict, link_bone_name_list: list, target_bone_name_list: list): if (link_bone_name_list[0] in org_model.bones and link_bone_name_list[0] in rep_model.bones) or \ (link_bone_name_list[1] in org_model.bones and link_bone_name_list[1] in rep_model.bones): # 元と先の両方に末端があればリンク作成 diff --git a/src/utils/MFileutils.py b/src/utils/MFileutils.py index 4e31ee7..5601ba7 100644 --- a/src/utils/MFileutils.py +++ b/src/utils/MFileutils.py @@ -246,73 +246,6 @@ def is_auto_camera_vmd_output_path(output_camera_vmd_path: str, motion_camera_vm return re.match(new_output_camera_vmd_pattern, output_camera_vmd_path) is not None -# スムージングVMD出力ファイルパス生成 -# base_file_path: モーションスムージングVMDパス -# pmx_path: 変換先モデルPMXパス -# output_smooth_vmd_path: 出力ファイルパス -def get_output_smooth_vmd_path(base_file_path: str, pmx_path: str, output_smooth_vmd_path: str, interpolation: int, loop_cnt: int, is_force=False): - # モーションスムージングVMDパスの拡張子リスト - if not os.path.exists(base_file_path) or not os.path.exists(pmx_path): - return "" - - # モーションスムージングVMDディレクトリパス - motion_smooth_vmd_dir_path = get_dir_path(base_file_path) - # モーションスムージングVMDファイル名・拡張子 - motion_smooth_vmd_file_name, motion_smooth_vmd_ext = os.path.splitext(os.path.basename(base_file_path)) - # 変換先モデルファイル名・拡張子 - rep_pmx_file_name, _ = os.path.splitext(os.path.basename(pmx_path)) - - # 補間方法 - suffix = "{0}{1}{2}".format( - ("F" if interpolation == 0 else ""), - ("C" if interpolation == 1 else ""), - ("V" if interpolation == 2 else ""), - ) - - if len(suffix) > 0: - suffix = "_{0}".format(suffix) - - suffix = "{0}_L{1}".format(suffix, loop_cnt) - - # 出力ファイルパス生成 - new_output_smooth_vmd_path = os.path.join(motion_smooth_vmd_dir_path, "{0}_{1}{2}_{3:%Y%m%d_%H%M%S}{4}".format(motion_smooth_vmd_file_name, rep_pmx_file_name, suffix, datetime.now(), ".vmd")) - - # ファイルパス自体が変更されたか、自動生成ルールに則っている場合、ファイルパス変更 - if is_force or is_auto_smooth_vmd_output_path(output_smooth_vmd_path, motion_smooth_vmd_dir_path, motion_smooth_vmd_file_name, ".vmd", rep_pmx_file_name): - - try: - open(new_output_smooth_vmd_path, 'w') - os.remove(new_output_smooth_vmd_path) - except Exception: - logger.warning("出力ファイルパスの生成に失敗しました。以下の原因が考えられます。\n" \ - + "・ファイルパスが255文字を超えている\n" \ - + "・ファイルパスに使えない文字列が含まれている(例) \\ / : * ? \" < > |)" \ - + "・出力ファイルパスの親フォルダに書き込み権限がない" \ - + "・出力ファイルパスに書き込み権限がない") - - return new_output_smooth_vmd_path - - return output_smooth_vmd_path - - -# 自動生成ルールに則ったパスか -def is_auto_smooth_vmd_output_path(output_smooth_vmd_path: str, motion_smooth_vmd_dir_path: str, motion_smooth_vmd_file_name: str, motion_smooth_vmd_ext: str, rep_pmx_file_name: str): - if not output_smooth_vmd_path: - # 出力パスがない場合、置き換え対象 - return True - - # 新しく設定しようとしている出力ファイルパスの正規表現 - escaped_motion_smooth_vmd_file_name = escape_filepath(os.path.join(motion_smooth_vmd_dir_path, motion_smooth_vmd_file_name)) - escaped_rep_pmx_file_name = escape_filepath(rep_pmx_file_name) - escaped_motion_smooth_vmd_ext = escape_filepath(motion_smooth_vmd_ext) - - new_output_smooth_vmd_pattern = re.compile(r'^%s_%s%s%s$' % (escaped_motion_smooth_vmd_file_name, \ - escaped_rep_pmx_file_name, r"_?\w*_L\d+_\d{8}_\d{6}", escaped_motion_smooth_vmd_ext)) - - # 自動生成ルールに則ったファイルパスである場合、合致あり - return re.match(new_output_smooth_vmd_pattern, output_smooth_vmd_path) is not None - - def escape_filepath(path: str): path = path.replace("\\", "\\\\") path = path.replace("*", "\\*") diff --git a/vmdising_np64.spec b/vmdising_np64.spec index f117323..b0fc597 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,7 +26,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β58_64bit', + name='VmdSizing_5.01_β59_64bit', debug=False, bootloader_ignore_signals=False, strip=False, From 6e1ee78475441ff139b38ce3093551cfef335e9c Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Wed, 28 Oct 2020 09:51:42 +0900 Subject: [PATCH 26/37] =?UTF-8?q?5.01=5F=CE=B260?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VmdSizing_5.01_β60 miumiu ・一括サイジング処理追加  ・設定例は、一括サイジングサンプル.csvとリドミ参照 ・スムージング・モーフブレンド・補間曲線ビューワーをモーションサポーターに移植 ・リドミをv5.01用にアップデート --- archive/Readme.txt | 105 +++- .../ver5.00\316\262\347\211\210Readme.txt" | 25 + ...5\343\203\263\343\203\227\343\203\253.csv" | 6 + ...4\343\203\236\343\203\203\343\203\210.csv" | 2 + ...\343\203\236\343\203\203\343\203\2102.csv" | 6 + src/executor.py | 2 +- src/form/MainFrame.py | 18 +- src/form/panel/ArmPanel.py | 7 + src/form/panel/BulkPanel.py | 595 ++++++++++++++++++ src/form/panel/CameraPanel.py | 21 +- src/form/panel/MorphPanel.py | 9 +- src/service/SizingService.py | 4 +- src/service/parts/ArmAvoidanceService.pyx | 2 +- src/service/parts/CameraService.py | 4 +- src/utils/MFileutils.py | 6 +- vmdising_np64.spec | 2 +- 16 files changed, 757 insertions(+), 57 deletions(-) create mode 100644 "archive/\344\270\200\346\213\254\343\202\265\343\202\244\343\202\270\343\203\263\343\202\260\343\202\265\343\203\263\343\203\227\343\203\253.csv" create mode 100644 "archive/\344\270\200\346\213\254\345\207\246\347\220\206\347\224\250\343\203\225\343\202\251\343\203\274\343\203\236\343\203\203\343\203\210.csv" create mode 100644 "archive/\344\270\200\346\213\254\345\207\246\347\220\206\347\224\250\343\203\225\343\202\251\343\203\274\343\203\236\343\203\203\343\203\2102.csv" create mode 100644 src/form/panel/BulkPanel.py diff --git a/archive/Readme.txt b/archive/Readme.txt index 129750b..8fc9e21 100644 --- a/archive/Readme.txt +++ b/archive/Readme.txt @@ -3,7 +3,7 @@ @uVMDTCWO@[JŁv -@@ver5.00 +@@ver5.01 @@@@@@@@@@@@@@@@@miu200521358 @@ -41,11 +41,11 @@ https://seiga.nicovideo.jp/seiga/im9755721 @t@C ---------------------------------------------------------------- -@EVMDSizing_5.00_64bit.exe@@@c@c[{́iver5.0064bitł݂̂łj +@EVMDSizing_5.01_64bit.exe@@@c@c[{́iver5.0064bitł݂̂łj @EReadme.txt@@@@@@@@@@c@h~ @EVMDTCWOWiki@@@@@@ c@Wikiւ̃N @E[tuTv@@@@@@c@[tũTvWiڍׂ͒QƁj - +@EꊇTCWOTv.csv@@c@ꊇTCWÕTvCSV ---------------------------------------------------------------- @‹ @@ -62,6 +62,8 @@ https://seiga.nicovideo.jp/seiga/im9755721 @nCXybNł́Aŕ񏈗ŝŁAʏł1.3{炢s܂B @Aׂ̕傫Ȃ̂ŁAPC̐\vĕ̕悳łB +EOo͔ł́Av4̍̂悤ɁAoVMDt@CpXƓƂɃOt@Co͂܂B + ---------------------------------------------------------------- @TCWO@\ @@ -142,7 +144,7 @@ https://seiga.nicovideo.jp/seiga/im9755721 EZ^[Y␳ @c@̘r̋AfƓ悤Ȉʒuɍ킹悤AZ^[𒲐܂B -@@@ver4,xx܂ł̏ʒu킹̈ꕔłB +@@@v4܂ł̏ʒu킹̈ꕔłB ---------------- @@ -199,31 +201,78 @@ A ---------------------------------------------------------------- -@܂@\ +@ꊇTCWO@\ ---------------------------------------------------------------- -1) X[WO - -Ew肳ꂽ[ṼL[tA炩Ɍq`ŐVɋOՂAVMDo͂܂B -EL[tԂ̕ԕ@́AOނIׂ܂B -@EԋȐɏ]@c@L[tɓo^ĂԋȐɏ]ĕԂ܂BL[̑SłƓłB -@EԋȐ𖳎i~`j@c@ԋȐ𖳎āAO_qɂȂ`ŋOՂ܂BXP[g[VƂɂg邩܂B -@EԋȐ𖳎iȐj@c@ԋȐ𖳎āAL[tJg}ȐŌqOՂ܂B炭ԔėpłB -EKpf̎w肪KvłBl̂ł@łłΉ”\łB -EX[WO񐔂ɏ]āAOՂɂxȂĂ܂B -@1񂾂ƑSłB2ōŒ̃L[ԈBȍ~̓tB^OŊԈ܂B - - ----------------- -2) [tuh +uꊇv^uŁACSVw肷鎖ŁÃTCWOꊇōsł܂B +̓IȒĺuꊇTCWOTv.csvvmFB +wb_̈sڂ͓ǂݔ΂܂B + +01 c O[vNo + TCWOsO[vPʂŘAԂ‚ĂB +02 c Ώۃ[VVMD/VPD(tpX) + ut@Cvuv^úuΏۃ[VVMD/VPDvt@CpXBAX^XN͎g܂B +03 c [V쐬fPMX + ut@Cvuv^úu[V쐬fPMXvt@CpXB +04 c [Vϊ惂fPMX + ut@Cvuv^úu[Vϊ惂fPMXvt@CpXB +05 c oVMD + ut@Cvuv^úuoVMDvB󗓂̏ꍇAΏۃ[VVMD/VPDƓKwɏo͂܂B +06 c Z^[XZ␳ + ut@Cvuv^úuX^Xlj␳Z^[XZ␳v`FbN{bNXB0:A1:L̂ꂩK{łB +07 c 㔼g␳ + ut@Cvuv^úuX^Xlj␳㔼g␳v`FbN{bNXB0:A1:L̂ꂩK{łB +08 c g␳ + ut@Cvuv^úuX^Xlj␳g␳v`FbN{bNXB0:A1:L̂ꂩK{łB +09 c hj␳ + ut@Cvuv^úuX^Xlj␳hj␳v`FbN{bNXB0:A1:L̂ꂩK{łB +10 c ‚ܐ␳ + ut@Cvuv^úuX^Xlj␳‚ܐ␳v`FbN{bNXB0:A1:L̂ꂩK{łB +11 c ‚ܐhj␳ + ut@Cvuv^úuX^Xlj␳‚ܐhj␳v`FbN{bNXB0:A1:L̂ꂩK{łB +12 c ␳ + ut@Cvuv^úuX^Xlj␳␳v`FbN{bNXB0:A1:L̂ꂩK{łB +13 c Z^[Y␳ + ut@Cvuv^úuX^Xlj␳Z^[Y␳v`FbN{bNXB0:A1:L̂ꂩK{łB +14 c 蕪U + ut@Cvuv^úu蕪Uv`FbN{bNXB0:A1:L̂ꂩK{łB +15 c [tu + u[tv^úu[tuṽZbgBu[t:u惂[t:傫;1‚̃ZbgƂĂB +16 c ڐG + urv^úuڐGv`FbN{bNXB0:A1:L̂ꂩK{łB +17 c ڐG + urv^úuڐGv̑Ώۍ̖XgB̖;̂悤ɔpZ~RŌqłB +18 c ʒu킹 + urv^úuʒu킹v`FbN{bNXB0:A1:L̂ꂩK{łB +19 c wʒu킹 + urv^úuẅʒuňʒu킹sv`FbN{bNXB0:A1:L̂ꂩK{łB +20 c ʒu킹 + urv^úuƂ̈ʒu킹ꏏɍsv`FbN{bNXB0:A1:L̂ꂩK{łB +21 c ̋ + urv^úuԂ̋vXC_[B0ȏ̒lK{łB +22 c w̋ + urv^úuwԂ̋vXC_[B0ȏ̒lK{łB +23 c Ƃ̋ + urv^úuƏƂ̋vXC_[B0ȏ̒lK{łB +24 c r`FbNXLbv + urv^úur`̃TCWO”\`FbNXLbvv`FbN{bNXB0:A1:L̂ꂩK{łB +25 c J[VVMD + uJv^úuJ[VVMDvt@CpXBO[vPʂ1sڂ̒l̂ݎQƂ܂B +26 c o̓JVMD + uJv^úuo̓JVMDvB󗓂̏ꍇAJ[VVMDƓKwɏo͂܂B +27 c “͈ + uJv^úu“͈́vXC_[B1ȏ̒lK{łB +28 c J쐬fPMX + uJv^úuJ쐬fPMXvt@CpXB +29 c SYItZbg + uJv^úuSYItZbgvB -Ew肳ꂽf́Aw肳ꂽ[t_ɑgݍ킹[Vf[^쐬܂B -Egݍ킹鐔ȂƔj]mオ̂ŒӂĂB -Ej]Ԃł̈ʌJ͔ĂB +---------------------------------------------------------------- +@܂@\ +---------------------------------------------------------------- ----------------- -3) CSVo +1) CSVo Ew肳ꂽVMD[Vf[^CSV`ŏo͂܂B E{[E[tEJʁX̃tH[}bgƂȂĂ܂B @@ -231,16 +280,12 @@ A ---------------- -4) VMDo +2) VMDo Ew肳ꂽCSVf[^iCSVo͋@\̃tH[}bgjVMD[Vf[^Ƃďo͂܂B EԋȐSďo͂܂B - ----------------- -5) ԋȐr[[ - -EMMD̕ԋȐ𕪊ǂȂ邩}ŕ\܂B +X[WOE[tuhEԋȐr[[́A[VT|[^[ɈڐA܂ ---------------------------------------------------------------- diff --git "a/archive/ver5.00\316\262\347\211\210Readme.txt" "b/archive/ver5.00\316\262\347\211\210Readme.txt" index e9befb2..5ff2853 100644 --- "a/archive/ver5.00\316\262\347\211\210Readme.txt" +++ "b/archive/ver5.00\316\262\347\211\210Readme.txt" @@ -10,6 +10,31 @@ ------------------------------------------------- ------------------------------------------------- +1) X[WO + +Ew肳ꂽ[ṼL[tA炩Ɍq`ŐVɋOՂAVMDo͂܂B +EL[tԂ̕ԕ@́AOނIׂ܂B +@EԋȐɏ]@c@L[tɓo^ĂԋȐɏ]ĕԂ܂BL[̑SłƓłB +@EԋȐ𖳎i~`j@c@ԋȐ𖳎āAO_qɂȂ`ŋOՂ܂BXP[g[VƂɂg邩܂B +@EԋȐ𖳎iȐj@c@ԋȐ𖳎āAL[tJg}ȐŌqOՂ܂B炭ԔėpłB +EKpf̎w肪KvłBl̂ł@łłΉ”\łB +EX[WO񐔂ɏ]āAOՂɂxȂĂ܂B +@1񂾂ƑSłB2ōŒ̃L[ԈBȍ~̓tB^OŊԈ܂B + + +---------------- +2) [tuh + +Ew肳ꂽf́Aw肳ꂽ[t_ɑgݍ킹[Vf[^쐬܂B +Egݍ킹鐔ȂƔj]mオ̂ŒӂĂB +Ej]Ԃł̈ʌJ͔ĂB + + + +---------------- +5) ԋȐr[[ + +EMMD̕ԋȐ𕪊ǂȂ邩}ŕ\܂B ---------------------------------------------------------------- diff --git "a/archive/\344\270\200\346\213\254\343\202\265\343\202\244\343\202\270\343\203\263\343\202\260\343\202\265\343\203\263\343\203\227\343\203\253.csv" "b/archive/\344\270\200\346\213\254\343\202\265\343\202\244\343\202\270\343\203\263\343\202\260\343\202\265\343\203\263\343\203\227\343\203\253.csv" new file mode 100644 index 0000000..2826bbd --- /dev/null +++ "b/archive/\344\270\200\346\213\254\343\202\265\343\202\244\343\202\270\343\203\263\343\202\260\343\202\265\343\203\263\343\203\227\343\203\253.csv" @@ -0,0 +1,6 @@ +O[vNo(l[V͓No),Ώۃ[VVMD/VPD(tpX),[V쐬fPMX(tpX),[Vϊ惂fPMX(tpX),oVMD(tpXA󗓃ftHݒ),Z^[XZ␳(0:A1:L),㔼g␳(0:A1:L),g␳(0:A1:L),hj␳(0:A1:L),‚ܐ␳(0:A1:L),‚ܐhj␳(0:A1:L),␳(0:A1:L),Z^[Y␳(0:A1:L),蕪U(0:ȂA1:),[tu(::傫;),ڐG(0:ȂA1:),ڐG(̖;),ʒu킹(0:ȂA1:),wʒu킹(0:ȂA1:),ʒu킹(0:ȂA1:),̋,w̋,Ƃ̋,r`FbNXLbv(0:ȂA1:),J[VVMD(tpXAO[v1ڂ̂),o̓JVMD(tpXA󗓃ftHݒ),“͈,J쐬fPMX(tpX),SYItZbg +1,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_1l\h}cM[ motion zzp moka\h}cM[_0-500.vmd,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\VOCALOID\~N\TdaftH~N_ver1.1 q\Tda~N_ftHver.pmx,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\\039_OclY\OclY |G v1.35\OclY_ʏ_W.pmx,,1,0,0,0,0,0,0,0,0,΂:ɂ:1;,0,,1,1,1,1.7,1.4,1.2,0,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_1l\h}cM[J 䂫˂\JWiTdaj_0-515.vmd,,5,,0 +2,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_1l\tIII Ȃ‚\nac_aikotoba3_0-500.vmd,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\VOCALOID\~N\Ԏ~N\Ԏ~N_W.pmx,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\cCXe\04_XJrA\J2007 ؂ȊP\ʏ탂f\J.pmx,,0,0,0,0,0,0,0,0,0,,0,,0,0,0,1.7,1.4,1.2,1,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_1l\tIII Ȃ‚\J[N.vmd,,5,,0 +3,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_2l\jA_vmd P.I.P\ėp_ȉ\jA_ėp_1377-1498.vmd,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\VOCALOID\~N\Tac~Nzzp\Tac~NVer1.3.1\ver1.3.1_m[}\Tac~Nver1.3.1im[}j.pmx,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\cCXe\06_COjnCh\CfAEVEh Ȃ\ȂCfA_20200813.pmx,,0,0,1,0,0,0,0,0,1,,0,,1,0,1,1.7,1.4,1.2,0,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_2l\jA_bv\Jzz_bym\ėp_ȉp\ȉpJ_0-1000.vmd,,5,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\VOCALOID\~N\߂ڂ ~N Ver1.11\߂ڂ ~N ver1.11.pmx,0 +3,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_2l\jA_vmd P.I.P\ėp_ȉ\jA_ėp_1377-1498.vmd,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\VOCALOID\~N\Tda~Ns[X\Tda~Ns[XRSP.pmx,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\cCXe\06_COjnCh\IgEVEh Im{\IgEVEh @A[L^CvEMA.pmx,,0,0,0,1,0,0,0,0,1,,0,,1,0,1,1.7,1.4,1.2,0,,,5,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\VOCALOID\~N\߂ڂ ~N Ver1.11\߂ڂ ~N ver1.11.pmx,0 +4,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_1l\ELECT yurie\GNg_2375-2420.vmd,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\Q[\퍑BASARA\K ʂ ver.1.24\^cKvߑ1.24yʔ.pmx,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\͑ꂭ\ۂՒav.2\ۂՒa_v.2_3.pmx,,0,1,0,0,0,0,0,0,0,,1,ڐG;ڐG,0,0,0,1.7,1.4,1.2,0,,,,, diff --git "a/archive/\344\270\200\346\213\254\345\207\246\347\220\206\347\224\250\343\203\225\343\202\251\343\203\274\343\203\236\343\203\203\343\203\210.csv" "b/archive/\344\270\200\346\213\254\345\207\246\347\220\206\347\224\250\343\203\225\343\202\251\343\203\274\343\203\236\343\203\203\343\203\210.csv" new file mode 100644 index 0000000..10086b3 --- /dev/null +++ "b/archive/\344\270\200\346\213\254\345\207\246\347\220\206\347\224\250\343\203\225\343\202\251\343\203\274\343\203\236\343\203\203\343\203\210.csv" @@ -0,0 +1,2 @@ +O[vNo(l[V͓No),Ώۃ[VVMD/VPD(tpX),[V쐬fPMX(tpX),[Vϊ惂fPMX(tpX),oVMD(tpXA󗓃ftHݒ),Z^[XZ␳(0:A1:L),㔼g␳(0:A1:L),g␳(0:A1:L),hj␳(0:A1:L),‚ܐ␳(0:A1:L),‚ܐhj␳(0:A1:L),␳(0:A1:L),Z^[Y␳(0:A1:L),蕪U(0:ȂA1:),r`FbNXLbv(0:ȂA1:),[tu(::傫;),ڐG(0:ȂA1:),ڐG(A;B),ʒu킹(0:ȂA1:),wʒu킹(0:ȂA1:),ʒu킹(0:ȂA1:),̋,w̋,̋,J[V(tpXAO[v1ڂ̂),o̓JVMD(tpXA󗓃ftHݒ),ғ͈,J쐬fPMX(tpX),SYItZbg +1,D:\MMD\UserFile\Model\[V.vmd,D:\MMD\UserFile\Model\~NVer2 W.pmx,D:\MMD\UserFile\Model\MEIKO.pmx,,1,1,1,1,1,1,1,1,1,0,"΂:ɂ:1,:߂:0.8",1,"ڐG,",1,1,1,1.7,1.4,1.2,D:\MMD\UserFile\Model\J.vmd,,5,D:\MMD\UserFile\Model\.pmx,0 diff --git "a/archive/\344\270\200\346\213\254\345\207\246\347\220\206\347\224\250\343\203\225\343\202\251\343\203\274\343\203\236\343\203\203\343\203\2102.csv" "b/archive/\344\270\200\346\213\254\345\207\246\347\220\206\347\224\250\343\203\225\343\202\251\343\203\274\343\203\236\343\203\203\343\203\2102.csv" new file mode 100644 index 0000000..2826bbd --- /dev/null +++ "b/archive/\344\270\200\346\213\254\345\207\246\347\220\206\347\224\250\343\203\225\343\202\251\343\203\274\343\203\236\343\203\203\343\203\2102.csv" @@ -0,0 +1,6 @@ +O[vNo(l[V͓No),Ώۃ[VVMD/VPD(tpX),[V쐬fPMX(tpX),[Vϊ惂fPMX(tpX),oVMD(tpXA󗓃ftHݒ),Z^[XZ␳(0:A1:L),㔼g␳(0:A1:L),g␳(0:A1:L),hj␳(0:A1:L),‚ܐ␳(0:A1:L),‚ܐhj␳(0:A1:L),␳(0:A1:L),Z^[Y␳(0:A1:L),蕪U(0:ȂA1:),[tu(::傫;),ڐG(0:ȂA1:),ڐG(̖;),ʒu킹(0:ȂA1:),wʒu킹(0:ȂA1:),ʒu킹(0:ȂA1:),̋,w̋,Ƃ̋,r`FbNXLbv(0:ȂA1:),J[VVMD(tpXAO[v1ڂ̂),o̓JVMD(tpXA󗓃ftHݒ),“͈,J쐬fPMX(tpX),SYItZbg +1,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_1l\h}cM[ motion zzp moka\h}cM[_0-500.vmd,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\VOCALOID\~N\TdaftH~N_ver1.1 q\Tda~N_ftHver.pmx,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\\039_OclY\OclY |G v1.35\OclY_ʏ_W.pmx,,1,0,0,0,0,0,0,0,0,΂:ɂ:1;,0,,1,1,1,1.7,1.4,1.2,0,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_1l\h}cM[J 䂫˂\JWiTdaj_0-515.vmd,,5,,0 +2,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_1l\tIII Ȃ‚\nac_aikotoba3_0-500.vmd,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\VOCALOID\~N\Ԏ~N\Ԏ~N_W.pmx,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\cCXe\04_XJrA\J2007 ؂ȊP\ʏ탂f\J.pmx,,0,0,0,0,0,0,0,0,0,,0,,0,0,0,1.7,1.4,1.2,1,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_1l\tIII Ȃ‚\J[N.vmd,,5,,0 +3,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_2l\jA_vmd P.I.P\ėp_ȉ\jA_ėp_1377-1498.vmd,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\VOCALOID\~N\Tac~Nzzp\Tac~NVer1.3.1\ver1.3.1_m[}\Tac~Nver1.3.1im[}j.pmx,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\cCXe\06_COjnCh\CfAEVEh Ȃ\ȂCfA_20200813.pmx,,0,0,1,0,0,0,0,0,1,,0,,1,0,1,1.7,1.4,1.2,0,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_2l\jA_bv\Jzz_bym\ėp_ȉp\ȉpJ_0-1000.vmd,,5,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\VOCALOID\~N\߂ڂ ~N Ver1.11\߂ڂ ~N ver1.11.pmx,0 +3,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_2l\jA_vmd P.I.P\ėp_ȉ\jA_ėp_1377-1498.vmd,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\VOCALOID\~N\Tda~Ns[X\Tda~Ns[XRSP.pmx,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\cCXe\06_COjnCh\IgEVEh Im{\IgEVEh @A[L^CvEMA.pmx,,0,0,0,1,0,0,0,0,1,,0,,1,0,1,1.7,1.4,1.2,0,,,5,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\VOCALOID\~N\߂ڂ ~N Ver1.11\߂ڂ ~N ver1.11.pmx,0 +4,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_1l\ELECT yurie\GNg_2375-2420.vmd,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\Q[\퍑BASARA\K ʂ ver.1.24\^cKvߑ1.24yʔ.pmx,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\͑ꂭ\ۂՒav.2\ۂՒa_v.2_3.pmx,,0,1,0,0,0,0,0,0,0,,1,ڐG;ڐG,0,0,0,1.7,1.4,1.2,0,,,,, diff --git a/src/executor.py b/src/executor.py index 2a2ed6f..890b838 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00_β59" +VERSION_NAME = "ver5.00_β60" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/form/MainFrame.py b/src/form/MainFrame.py index c3fb6a6..6026de2 100644 --- a/src/form/MainFrame.py +++ b/src/form/MainFrame.py @@ -12,6 +12,7 @@ from form.panel.CameraPanel import CameraPanel from form.panel.CsvPanel import CsvPanel from form.panel.VmdPanel import VmdPanel +from form.panel.BulkPanel import BulkPanel from form.worker.SizingWorkerThread import SizingWorkerThread from form.worker.LoadWorkerThread import LoadWorkerThread from module.MMath import MRect, MVector3D, MVector4D, MQuaternion, MMatrix4x4 # noqa @@ -96,12 +97,16 @@ def __init__(self, parent, mydir_path: str, version_name: str, logging_level: in self.camera_panel_ctrl = CameraPanel(self, self.note_ctrl, 4) self.note_ctrl.AddPage(self.camera_panel_ctrl, u"カメラ", False) + # 一括タブ + self.bulk_panel_ctrl = BulkPanel(self, self.note_ctrl, 5) + self.note_ctrl.AddPage(self.bulk_panel_ctrl, u"一括", False) + # CSVタブ - self.csv_panel_ctrl = CsvPanel(self, self.note_ctrl, 7) + self.csv_panel_ctrl = CsvPanel(self, self.note_ctrl, 6) self.note_ctrl.AddPage(self.csv_panel_ctrl, u"CSV", False) # VMDタブ - self.vmd_panel_ctrl = VmdPanel(self, self.note_ctrl, 8) + self.vmd_panel_ctrl = VmdPanel(self, self.note_ctrl, 7) self.note_ctrl.AddPage(self.vmd_panel_ctrl, u"VMD", False) # --------------------------------------------- @@ -161,6 +166,11 @@ def on_tab_change(self, event: wx.Event): event.Skip() return + elif self.bulk_panel_ctrl.is_fix_tab: + self.note_ctrl.ChangeSelection(self.bulk_panel_ctrl.tab_idx) + event.Skip() + return + if self.note_ctrl.GetSelection() == self.multi_panel_ctrl.tab_idx: # 複数タブ移動時に保存 self.file_panel_ctrl.save() @@ -203,11 +213,13 @@ def release_tab(self): self.file_panel_ctrl.release_tab() self.morph_panel_ctrl.release_tab() self.arm_panel_ctrl.release_tab() + self.multi_panel_ctrl.release_tab() + self.bulk_panel_ctrl.release_tab() # フォーム入力可 def enable(self): self.file_panel_ctrl.enable() - # self.morph_panel_ctrl.enable() + self.bulk_panel_ctrl.enable() # ファイルセットの入力可否チェック def is_valid(self): diff --git a/src/form/panel/ArmPanel.py b/src/form/panel/ArmPanel.py index 92fc27f..6e1cdcf 100644 --- a/src/form/panel/ArmPanel.py +++ b/src/form/panel/ArmPanel.py @@ -27,6 +27,9 @@ def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int): + "「頭接触回避」は頭を中心とした球体剛体を自動で計算します。" alignment_tooltip = "変換先モデルの手首位置が、作成元モデルの手首とほぼ同じ位置になるよう、手首位置を調整します。" + # Bulk用接触回避データ + self.bulk_avoidance_set_dict = {} + # 同じグループなので、とりあえず宣言だけしておく self.arm_process_flg_avoidance = wx.CheckBox(self, wx.ID_ANY, u"", wx.DefaultPosition, wx.DefaultSize) self.arm_process_flg_avoidance.SetToolTip(avoidance_tooltip) @@ -195,6 +198,10 @@ def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int): self.fit() def get_avoidance_target(self): + if len(self.bulk_avoidance_set_dict.keys()) > 0: + # Bulk用データがある場合、優先返還 + return self.bulk_avoidance_set_dict + target = {} if self.arm_process_flg_avoidance.GetValue() == 0: return target diff --git a/src/form/panel/BulkPanel.py b/src/form/panel/BulkPanel.py new file mode 100644 index 0000000..fc8eda7 --- /dev/null +++ b/src/form/panel/BulkPanel.py @@ -0,0 +1,595 @@ +# -*- coding: utf-8 -*- +# +import wx +import wx.lib.newevent +import sys +import csv +import re +import os + +from form.panel.BasePanel import BasePanel +from form.parts.HistoryFilePickerCtrl import HistoryFilePickerCtrl +from form.parts.ConsoleCtrl import ConsoleCtrl +from form.worker.SizingWorkerThread import SizingWorkerThread +from form.worker.LoadWorkerThread import LoadWorkerThread +from utils import MFormUtils, MFileUtils # noqa +from utils.MLogger import MLogger # noqa + +logger = MLogger(__name__) +TIMER_ID = wx.NewId() + +# イベント +(BulkSizingThreadEvent, EVT_BULK_SIZING_THREAD) = wx.lib.newevent.NewEvent() +(BulkLoadThreadEvent, EVT_BULK_LOAD_THREAD) = wx.lib.newevent.NewEvent() + + +class BulkPanel(BasePanel): + + def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int): + super().__init__(frame, parent, tab_idx) + + self.description_txt = wx.StaticText(self, wx.ID_ANY, "設定を一括で指定して、連続して処理させる事ができます。", wx.DefaultPosition, wx.DefaultSize, 0) + self.sizer.Add(self.description_txt, 0, wx.ALL, 5) + + self.static_line = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + self.sizer.Add(self.static_line, 0, wx.EXPAND | wx.ALL, 5) + + # バルクBULKファイルコントロール + self.bulk_csv_file_ctrl = HistoryFilePickerCtrl(frame, self, u"一括処理用CSV", u"一括処理用CSVファイルを開く", ("csv"), wx.FLP_DEFAULT_STYLE, \ + u"一括処理用のCSVを指定してください。\nフォーマットは、DLボタンから取得できます。\nD&Dでの指定、開くボタンからの指定、履歴からの選択ができます。", \ + file_model_spacer=0, title_parts_ctrl=None, title_parts2_ctrl=None, \ + file_histories_key="bulk_csv", is_change_output=False, is_aster=False, is_save=False, set_no=0) + self.sizer.Add(self.bulk_csv_file_ctrl.sizer, 0, wx.EXPAND | wx.ALL, 0) + + btn_sizer = wx.BoxSizer(wx.HORIZONTAL) + + # 一括サイジング実行ボタン + self.bulk_btn_ctrl = wx.Button(self, wx.ID_ANY, u"一括サイジング実行", wx.DefaultPosition, wx.Size(200, 50), 0) + self.bulk_btn_ctrl.SetToolTip(u"一括でサイジングを実行します") + self.bulk_btn_ctrl.Bind(wx.EVT_LEFT_DCLICK, self.on_doubleclick) + self.bulk_btn_ctrl.Bind(wx.EVT_LEFT_DOWN, self.on_bulk_click) + btn_sizer.Add(self.bulk_btn_ctrl, 0, wx.ALL, 5) + + self.sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER | wx.SHAPED, 5) + + # コンソール + self.console_ctrl = ConsoleCtrl(self, self.frame.logging_level, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(-1, 420), \ + wx.TE_MULTILINE | wx.TE_READONLY | wx.BORDER_NONE | wx.HSCROLL | wx.VSCROLL | wx.WANTS_CHARS) + self.console_ctrl.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT)) + self.console_ctrl.Bind(wx.EVT_CHAR, lambda event: MFormUtils.on_select_all(event, self.console_ctrl)) + self.sizer.Add(self.console_ctrl, 1, wx.ALL | wx.EXPAND, 5) + + # ゲージ + self.gauge_ctrl = wx.Gauge(self, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.GA_HORIZONTAL) + self.gauge_ctrl.SetValue(0) + self.sizer.Add(self.gauge_ctrl, 0, wx.ALL | wx.EXPAND, 5) + + self.fit() + + # 変換完了処理バインド + self.frame.Bind(EVT_BULK_LOAD_THREAD, self.on_load_result) + self.frame.Bind(EVT_BULK_SIZING_THREAD, self.on_exec_result) + + # フォーム無効化 + def disable(self): + self.bulk_csv_file_ctrl.disable() + self.bulk_btn_ctrl.Disable() + + # フォーム無効化 + def enable(self): + self.bulk_csv_file_ctrl.enable() + self.bulk_btn_ctrl.Enable() + + def on_doubleclick(self, event: wx.Event): + self.timer.Stop() + logger.warning("ダブルクリックされました。", decoration=MLogger.DECORATION_BOX) + event.Skip(False) + return False + + def on_bulk_click(self, event: wx.Event): + self.timer = wx.Timer(self, TIMER_ID) + self.timer.Start(200) + self.Bind(wx.EVT_TIMER, self.on_bulk, id=TIMER_ID) + + # サイジング一括実行 + def on_bulk(self, event: wx.Event): + if self.timer: + self.timer.Stop() + self.Unbind(wx.EVT_TIMER, id=TIMER_ID) + + # 出力先をファイルパネルのコンソールに変更 + sys.stdout = self.console_ctrl + + if self.bulk_btn_ctrl.GetLabel() == "一括サイジング停止" and self.frame.worker: + # フォーム無効化 + self.disable() + # 停止状態でボタン押下時、停止 + self.frame.worker.stop() + + # タブ移動可 + self.release_tab() + # フォーム有効化 + self.enable() + # ワーカー終了 + self.frame.worker = None + # プログレス非表示 + self.gauge_ctrl.SetValue(0) + + logger.warning("VMDサイジング一括処理を中断します。", decoration=MLogger.DECORATION_BOX) + + event.Skip(False) + elif not self.frame.worker: + # フォーム無効化 + self.disable() + # タブ固定 + self.fix_tab() + # コンソールクリア + self.console_ctrl.Clear() + + # 履歴保持 + self.save() + + # サイジング可否チェックの後に実行 + self.check(event) + + event.Skip() + else: + logger.error("まだ処理が実行中です。終了してから再度実行してください。", decoration=MLogger.DECORATION_BOX) + event.Skip(False) + + def save(self): + + # 履歴保持 + self.bulk_csv_file_ctrl.save() + + # JSON出力 + MFileUtils.save_history(self.frame.mydir_path, self.frame.file_hitories) + + # データチェック + def check(self, event: wx.Event): + # フォーム無効化 + self.disable() + # タブ固定 + self.fix_tab() + + result = True + with open(self.bulk_csv_file_ctrl.path(), encoding='cp932', mode='r') as f: + reader = csv.reader(f) + next(reader) # ヘッダーを読み飛ばす + + for ridx, rows in enumerate(reader): + row_no = ridx + group_no_result, group_no = self.read_csv_row(rows, row_no, 0, "グループNo", True, int, r"\d+", "数値のみ", None) + org_motion_result, org_motion_path = self.read_csv_row(rows, row_no, 1, "調整対象モーションVMD/VPD", True, str, None, None, ("vmd", "vpd")) + org_model_result, org_model_path = self.read_csv_row(rows, row_no, 2, "モーション作成元モデルPMX", True, str, None, None, ("pmx")) + rep_model_result, rep_model_path = self.read_csv_row(rows, row_no, 3, "モーション変換先モデルPMX", True, str, None, None, ("pmx")) + output_motion_result, output_motion_path = self.read_csv_row(rows, row_no, 4, "出力VMD", False, str, None, None, ("vmd")) + stance_center_xz_result, stance_center_xz_datas = self.read_csv_row(rows, row_no, 5, "センターXZ補正", True, int, r"(0|1)", "0 もしくは 1", None) + stance_upper_result, stance_upper_datas = self.read_csv_row(rows, row_no, 6, "上半身補正", True, int, r"(0|1)", "0 もしくは 1", None) + stance_lower_result, stance_lower_datas = self.read_csv_row(rows, row_no, 7, "下半身補正", True, int, r"(0|1)", "0 もしくは 1", None) + stance_leg_ik_result, stance_leg_ik_datas = self.read_csv_row(rows, row_no, 8, "足IK補正", True, int, r"(0|1)", "0 もしくは 1", None) + stance_toe_result, stance_toe_datas = self.read_csv_row(rows, row_no, 9, "つま先補正", True, int, r"(0|1)", "0 もしくは 1", None) + stance_toe_ik_result, stance_toe_ik_datas = self.read_csv_row(rows, row_no, 10, "つま先IK補正", True, int, r"(0|1)", "0 もしくは 1", None) + stance_shoulder_result, stance_shoulder_datas = self.read_csv_row(rows, row_no, 11, "肩補正", True, int, r"(0|1)", "0 もしくは 1", None) + stance_center_y_result, stance_center_y_datas = self.read_csv_row(rows, row_no, 12, "センターY補正", True, int, r"(0|1)", "0 もしくは 1", None) + separate_twist_result, separate_twist_datas = self.read_csv_row(rows, row_no, 13, "捩り分散", True, int, r"(0|1)", "0 もしくは 1", None) + morph_result, morph_datas = self.read_csv_row(rows, row_no, 14, "モーフ置換", False, str, r"[^\:]+\:[^\:]+\:\d+\.?\d*\;", "元:先:大きさ;", None) + arm_avoidance_result, arm_avoidance_datas = self.read_csv_row(rows, row_no, 15, "接触回避", True, int, r"(0|1)", "0 もしくは 1", None) + avoidance_name_result, avoidance_name_datas = self.read_csv_row(rows, row_no, 16, "接触回避剛体", False, str, r"[^\;]+\;", "剛体名;", None) + arm_alignment_result, arm_alignment_datas = self.read_csv_row(rows, row_no, 17, "位置合わせ", True, int, r"(0|1)", "0 もしくは 1", None) + finger_alignment_result, finger_alignment_datas = self.read_csv_row(rows, row_no, 18, "指位置合わせ", False, int, r"(0|1)", "0 もしくは 1", None) + floor_alignment_result, floor_alignment_datas = self.read_csv_row(rows, row_no, 19, "床位置合わせ", False, int, r"(0|1)", "0 もしくは 1", None) + arm_alignment_length_result, arm_alignment_length_datas = self.read_csv_row(rows, row_no, 20, "手首の距離", False, float, None, None, None) + finger_alignment_length_result, finger_alignment_length_datas = self.read_csv_row(rows, row_no, 21, "指の距離", False, float, None, None, None) + floor_alignment_length_result, floor_alignment_length_datas = self.read_csv_row(rows, row_no, 22, "床との距離", False, float, None, None, None) + arm_check_skip_result, arm_check_skip_datas = self.read_csv_row(rows, row_no, 23, "腕チェックスキップ", True, int, r"(0|1)", "0 もしくは 1", None) + org_camera_motion_result, org_camera_motion_path = self.read_csv_row(rows, row_no, 24, "カメラモーションVMD", False, str, None, None, ("vmd")) + output_camera_motion_result, output_camera_motion_path = self.read_csv_row(rows, row_no, 25, "出力カメラVMD", False, str, None, None, ("vmd")) + camera_length_result, camera_length_datas = self.read_csv_row(rows, row_no, 26, "距離稼働範囲", False, float, None, None, None) + org_camera_model_result, org_camera_model_path = self.read_csv_row(rows, row_no, 27, "カメラ作成元モデルPMX", False, str, None, None, ("pmx")) + camera_y_offset_result, camera_y_offset_datas = self.read_csv_row(rows, row_no, 28, "全長Yオフセット", False, float, None, None, None) + + result = result & group_no_result & org_motion_result & org_model_result & rep_model_result & output_motion_result & stance_center_xz_result \ + & stance_upper_result & stance_lower_result & stance_leg_ik_result & stance_toe_result & stance_toe_ik_result & stance_shoulder_result \ + & stance_center_y_result & separate_twist_result & arm_check_skip_result & morph_result & arm_avoidance_result & avoidance_name_result \ + & arm_alignment_result & finger_alignment_result & floor_alignment_result & arm_alignment_length_result & finger_alignment_length_result \ + & floor_alignment_length_result & org_camera_motion_result & output_camera_motion_result & camera_length_result & org_camera_model_result \ + & camera_y_offset_result + + if result: + # 全部OKなら処理開始 + self.load(event, 0) + else: + logger.error("CSVデータに不整合があるため、処理を中断します") + return + + def read_csv_row(self, rows: list, row_no: int, row_idx: int, row_name: str, row_required: bool, row_type: type, row_regex: str, row_regex_str: str, path_exts: tuple): + try: + if row_required and (len(rows) < row_idx or not rows[row_idx]): + logger.warning("%s行目の%s(%s列目)が設定されていません", row_no, row_name, row_idx + 1) + return False, None + + try: + if rows[row_idx] and not row_type(rows[row_idx]): + pass + except Exception: + row_type_str = "半角整数" if row_type == int else "半角数字" + logger.warning("%s行目の%s(%s列目)の型(%s)が合っていません", row_no, row_name, row_idx + 1, row_type_str) + return False, None + + if rows[row_idx] and row_regex and not re.findall(row_regex, rows[row_idx]): + logger.warning("%s行目の%s(%s列目)の表示形式(%s)が合っていません", row_no, row_name, row_idx + 1, row_regex_str) + return False, None + + if rows[row_idx] and path_exts: + if (not os.path.exists(rows[row_idx]) or not os.path.isfile(rows[row_idx])): + logger.warning("%s行目の%s(%s列目)のファイルが存在していません", row_no, row_name, row_idx + 1) + return False, None + + # ファイル名・拡張子 + file_name, ext = os.path.splitext(os.path.basename(rows[row_idx])) + if (not file_name and ext not in path_exts): + logger.warning("%s行目の%s(%s列目)のファイル拡張子(%s)が合っていません", row_no, row_name, row_idx + 1, ','.join(map(str, path_exts))) + return False, None + + # 読み取り実施 + if rows[row_idx] and row_regex: + # 正規表現の場合は、リスト変換して返す + if row_type: + # 型指定がある場合は変換して返す + return True, [row_type(v) for v in re.findall(row_regex, rows[row_idx])] + else: + return True, re.findall(row_regex, rows[row_idx]) + + if (row_type == float or row_type == int) and not rows[row_idx]: + # 数値で任意はゼロ設定 + return True, 0 + elif row_type: + return True, row_type(rows[row_idx]) + + return True, rows[row_idx] + except Exception as e: + logger.warning("%s行目の%s(%s列目)の読み取りに失敗しました\n%s", row_no, row_name, row_idx + 1, e) + return False, None + + # 読み込み + def load(self, event, line_idx): + # グループ単位で設定 + now_group_no = -1 + now_motion_idx = -1 + row_no = 0 + with open(self.bulk_csv_file_ctrl.path(), encoding='cp932', mode='r') as f: + reader = csv.reader(f) + next(reader) # ヘッダーを読み飛ばす + + for ridx, rows in enumerate(reader): + row_no = ridx + + if row_no < line_idx: + # 自分より前の行の場合、スキップ + continue + + group_no_result, group_no = self.read_csv_row(rows, row_no, 0, "グループNo", True, int, r"\d+", "数値のみ", None) + + if len(group_no) == 0: + # グループNOが取れなかったから終了 + return + + if len(group_no) > 0 and row_no == line_idx: + # 指定INDEXに到達したら設定して読み取り開始 + now_motion_idx = 0 + now_group_no = group_no[0] + else: + now_motion_idx += 1 + + if len(group_no) > 0 and group_no[0] != now_group_no: + # グループNOが変わっていたら、そのまま終了 + break + + group_no_result, group_no = self.read_csv_row(rows, row_no, 0, "グループNo", True, int, r"\d+", "数値のみ", None) + org_motion_result, org_motion_path = self.read_csv_row(rows, row_no, 1, "調整対象モーションVMD/VPD", True, str, None, None, ("vmd", "vpd")) + org_model_result, org_model_path = self.read_csv_row(rows, row_no, 2, "モーション作成元モデルPMX", True, str, None, None, ("pmx")) + rep_model_result, rep_model_path = self.read_csv_row(rows, row_no, 3, "モーション変換先モデルPMX", True, str, None, None, ("pmx")) + output_motion_result, output_motion_path = self.read_csv_row(rows, row_no, 4, "出力VMD", False, str, None, None, ("vmd")) + stance_center_xz_result, stance_center_xz_datas = self.read_csv_row(rows, row_no, 5, "センターXZ補正", True, int, r"(0|1)", "0 もしくは 1", None) + stance_upper_result, stance_upper_datas = self.read_csv_row(rows, row_no, 6, "上半身補正", True, int, r"(0|1)", "0 もしくは 1", None) + stance_lower_result, stance_lower_datas = self.read_csv_row(rows, row_no, 7, "下半身補正", True, int, r"(0|1)", "0 もしくは 1", None) + stance_leg_ik_result, stance_leg_ik_datas = self.read_csv_row(rows, row_no, 8, "足IK補正", True, int, r"(0|1)", "0 もしくは 1", None) + stance_toe_result, stance_toe_datas = self.read_csv_row(rows, row_no, 9, "つま先補正", True, int, r"(0|1)", "0 もしくは 1", None) + stance_toe_ik_result, stance_toe_ik_datas = self.read_csv_row(rows, row_no, 10, "つま先IK補正", True, int, r"(0|1)", "0 もしくは 1", None) + stance_shoulder_result, stance_shoulder_datas = self.read_csv_row(rows, row_no, 11, "肩補正", True, int, r"(0|1)", "0 もしくは 1", None) + stance_center_y_result, stance_center_y_datas = self.read_csv_row(rows, row_no, 12, "センターY補正", True, int, r"(0|1)", "0 もしくは 1", None) + separate_twist_result, separate_twist_datas = self.read_csv_row(rows, row_no, 13, "捩り分散", True, int, r"(0|1)", "0 もしくは 1", None) + morph_result, morph_datas = self.read_csv_row(rows, row_no, 14, "モーフ置換", False, str, r"[^\:]+\:[^\:]+\:\d+\.?\d*\;", "元:先:大きさ;", None) + arm_avoidance_result, arm_avoidance_datas = self.read_csv_row(rows, row_no, 15, "接触回避", True, int, r"(0|1)", "0 もしくは 1", None) + avoidance_name_result, avoidance_name_datas = self.read_csv_row(rows, row_no, 16, "接触回避剛体", False, str, r"[^\;]+\;", "剛体名;", None) + arm_alignment_result, arm_alignment_datas = self.read_csv_row(rows, row_no, 17, "位置合わせ", True, int, r"(0|1)", "0 もしくは 1", None) + finger_alignment_result, finger_alignment_datas = self.read_csv_row(rows, row_no, 18, "指位置合わせ", False, int, r"(0|1)", "0 もしくは 1", None) + floor_alignment_result, floor_alignment_datas = self.read_csv_row(rows, row_no, 19, "床位置合わせ", False, int, r"(0|1)", "0 もしくは 1", None) + arm_alignment_length_result, arm_alignment_length_datas = self.read_csv_row(rows, row_no, 20, "手首の距離", False, float, None, None, None) + finger_alignment_length_result, finger_alignment_length_datas = self.read_csv_row(rows, row_no, 21, "指の距離", False, float, None, None, None) + floor_alignment_length_result, floor_alignment_length_datas = self.read_csv_row(rows, row_no, 22, "床との距離", False, float, None, None, None) + arm_check_skip_result, arm_check_skip_datas = self.read_csv_row(rows, row_no, 23, "腕チェックスキップ", True, int, r"(0|1)", "0 もしくは 1", None) + org_camera_motion_result, org_camera_motion_path = self.read_csv_row(rows, row_no, 24, "カメラモーションVMD", False, str, None, None, ("vmd")) + output_camera_motion_result, output_camera_motion_path = self.read_csv_row(rows, row_no, 25, "出力カメラVMD", False, str, None, None, ("vmd")) + camera_length_result, camera_length_datas = self.read_csv_row(rows, row_no, 26, "距離稼働範囲", False, float, None, None, None) + org_camera_model_result, org_camera_model_path = self.read_csv_row(rows, row_no, 27, "カメラ作成元モデルPMX", False, str, None, None, ("pmx")) + camera_y_offset_result, camera_y_offset_datas = self.read_csv_row(rows, row_no, 28, "全長Yオフセット", False, float, None, None, None) + + if now_motion_idx == 0: + # 複数パネルはクリア + self.frame.multi_panel_ctrl.on_clear_set(event) + + # ファイルパネル設定 + self.frame.file_panel_ctrl.file_set.motion_vmd_file_ctrl.file_ctrl.SetPath(org_motion_path) + self.frame.file_panel_ctrl.file_set.org_model_file_ctrl.file_ctrl.SetPath(org_model_path) + self.frame.file_panel_ctrl.file_set.rep_model_file_ctrl.file_ctrl.SetPath(rep_model_path) + self.frame.file_panel_ctrl.file_set.output_vmd_file_ctrl.file_ctrl.SetPath(output_motion_path) + + self.frame.file_panel_ctrl.file_set.org_model_file_ctrl.title_parts_ctrl.SetValue( + stance_center_xz_datas[0] | stance_upper_datas[0] | stance_lower_datas[0] | stance_leg_ik_datas[0] | \ + stance_toe_datas[0] | stance_toe_ik_datas[0] | stance_shoulder_datas[0] | stance_center_y_datas[0] + ) + + # スタンス追加補正 + self.frame.file_panel_ctrl.file_set.selected_stance_details = [] + if stance_center_xz_datas[0] == 1: + self.frame.file_panel_ctrl.file_set.selected_stance_details.append(0) + if stance_upper_datas[0] == 1: + self.frame.file_panel_ctrl.file_set.selected_stance_details.append(1) + if stance_lower_datas[0] == 1: + self.frame.file_panel_ctrl.file_set.selected_stance_details.append(2) + if stance_leg_ik_datas[0] == 1: + self.frame.file_panel_ctrl.file_set.selected_stance_details.append(3) + if stance_toe_datas[0] == 1: + self.frame.file_panel_ctrl.file_set.selected_stance_details.append(4) + if stance_toe_ik_datas[0] == 1: + self.frame.file_panel_ctrl.file_set.selected_stance_details.append(5) + if stance_shoulder_datas[0] == 1: + self.frame.file_panel_ctrl.file_set.selected_stance_details.append(6) + if stance_center_y_datas[0] == 1: + self.frame.file_panel_ctrl.file_set.selected_stance_details.append(7) + + # 捩り分散 + self.frame.file_panel_ctrl.file_set.rep_model_file_ctrl.title_parts_ctrl.SetValue(separate_twist_datas[0]) + + # 腕チェックスキップ + self.frame.arm_panel_ctrl.arm_check_skip_flg_ctrl.SetValue(arm_check_skip_datas[0]) + + # モーフデータ + self.frame.morph_panel_ctrl.bulk_morph_set_dict[1] = [] + for morph_data in morph_datas: + m = re.findall(r"([^\:]+)\:([^\:]+)\:(\d+\.?\d*)\;", morph_data) + self.frame.morph_panel_ctrl.bulk_morph_set_dict[1].append((m[0][0], m[0][1], float(m[0][2]))) + + # 接触回避 + self.frame.arm_panel_ctrl.arm_process_flg_avoidance.SetValue(arm_avoidance_datas[0]) + + # 接触回避データ + self.frame.arm_panel_ctrl.bulk_avoidance_set_dict[0] = [] + for avoidance_data in avoidance_name_datas: + m = re.findall(r"([^\:]+)\;", avoidance_data) + self.frame.arm_panel_ctrl.bulk_avoidance_set_dict[0].append(m[0][0]) + + # 位置合わせ + self.frame.arm_panel_ctrl.arm_process_flg_alignment.SetValue(arm_alignment_datas[0]) + self.frame.arm_panel_ctrl.arm_alignment_finger_flg_ctrl.SetValue(finger_alignment_datas[0]) + self.frame.arm_panel_ctrl.arm_alignment_floor_flg_ctrl.SetValue(floor_alignment_datas[0]) + + # 位置合わせ距離 + self.frame.arm_panel_ctrl.alignment_distance_wrist_slider.SetValue(arm_alignment_length_datas) + self.frame.arm_panel_ctrl.alignment_distance_finger_slider.SetValue(finger_alignment_length_datas) + self.frame.arm_panel_ctrl.alignment_distance_floor_slider.SetValue(floor_alignment_length_datas) + + # カメラ + self.frame.camera_panel_ctrl.camera_vmd_file_ctrl.file_ctrl.SetPath(org_camera_motion_path) + self.frame.camera_panel_ctrl.output_camera_vmd_file_ctrl.file_ctrl.SetPath(output_camera_motion_path) + self.frame.camera_panel_ctrl.camera_length_slider.SetValue(camera_length_datas) + + # カメラ元情報 + self.frame.camera_panel_ctrl.initialize(event) + self.frame.camera_panel_ctrl.camera_set_dict[1].camera_model_file_ctrl.file_ctrl.SetPath(org_camera_model_path) + self.frame.camera_panel_ctrl.camera_set_dict[1].camera_offset_y_ctrl.SetValue(camera_y_offset_datas) + + # 出力パス変更 + self.frame.file_panel_ctrl.file_set.set_output_vmd_path(event) + self.frame.camera_panel_ctrl.set_output_vmd_path(event) + else: + # 複数パネルセット追加 + self.frame.multi_panel_ctrl.on_add_set(event) + + # ファイルパネル設定 + self.frame.multi_panel_ctrl.file_set_list[now_motion_idx - 1].motion_vmd_file_ctrl.file_ctrl.SetPath(org_motion_path) + self.frame.multi_panel_ctrl.file_set_list[now_motion_idx - 1].org_model_file_ctrl.file_ctrl.SetPath(org_model_path) + self.frame.multi_panel_ctrl.file_set_list[now_motion_idx - 1].rep_model_file_ctrl.file_ctrl.SetPath(rep_model_path) + self.frame.multi_panel_ctrl.file_set_list[now_motion_idx - 1].output_vmd_file_ctrl.file_ctrl.SetPath(output_motion_path) + + self.frame.multi_panel_ctrl.file_set_list[now_motion_idx - 1].org_model_file_ctrl.title_parts_ctrl.SetValue( + stance_center_xz_datas[0] | stance_upper_datas[0] | stance_lower_datas[0] | stance_leg_ik_datas[0] | \ + stance_toe_datas[0] | stance_toe_ik_datas[0] | stance_shoulder_datas[0] | stance_center_y_datas[0] + ) + + # スタンス追加補正 + self.frame.multi_panel_ctrl.file_set_list[now_motion_idx - 1].selected_stance_details = [] + if stance_center_xz_datas[0] == 1: + self.frame.multi_panel_ctrl.file_set_list[now_motion_idx - 1].selected_stance_details.append(0) + if stance_upper_datas[0] == 1: + self.frame.multi_panel_ctrl.file_set_list[now_motion_idx - 1].selected_stance_details.append(1) + if stance_lower_datas[0] == 1: + self.frame.multi_panel_ctrl.file_set_list[now_motion_idx - 1].selected_stance_details.append(2) + if stance_leg_ik_datas[0] == 1: + self.frame.multi_panel_ctrl.file_set_list[now_motion_idx - 1].selected_stance_details.append(3) + if stance_toe_datas[0] == 1: + self.frame.multi_panel_ctrl.file_set_list[now_motion_idx - 1].selected_stance_details.append(4) + if stance_toe_ik_datas[0] == 1: + self.frame.multi_panel_ctrl.file_set_list[now_motion_idx - 1].selected_stance_details.append(5) + if stance_shoulder_datas[0] == 1: + self.frame.multi_panel_ctrl.file_set_list[now_motion_idx - 1].selected_stance_details.append(6) + if stance_center_y_datas[0] == 1: + self.frame.multi_panel_ctrl.file_set_list[now_motion_idx - 1].selected_stance_details.append(7) + + # 捩り分散 + self.frame.multi_panel_ctrl.file_set_list[now_motion_idx - 1].rep_model_file_ctrl.title_parts_ctrl.SetValue(separate_twist_datas[0]) + + # モーフデータ + self.frame.morph_panel_ctrl.bulk_morph_set_dict[now_motion_idx + 1] = [] + for morph_data in morph_datas: + m = re.findall(r"([^\:]+)\:([^\:]+)\:(\d+\.?\d*)\;", morph_data) + self.frame.morph_panel_ctrl.bulk_morph_set_dict[now_motion_idx + 1].append((m[0][0], m[0][1], float(m[0][2]))) + + # 接触回避データ + self.frame.arm_panel_ctrl.bulk_avoidance_set_dict[now_motion_idx - 1] = [] + for avoidance_data in avoidance_name_datas: + m = re.findall(r"([^\:]+)\;", avoidance_data) + self.frame.arm_panel_ctrl.bulk_avoidance_set_dict[now_motion_idx - 1].append(m[0][0]) + + # カメラ元情報 + self.frame.camera_panel_ctrl.initialize(event) + self.frame.camera_panel_ctrl.camera_set_dict[now_motion_idx + 1].camera_model_file_ctrl.file_ctrl.SetPath(org_camera_model_path) + self.frame.camera_panel_ctrl.camera_set_dict[now_motion_idx + 1].camera_offset_y_ctrl.SetValue(camera_y_offset_datas) + + # 出力パス変更 + self.frame.multi_panel_ctrl.file_set_list[now_motion_idx - 1].set_output_vmd_path(event) + + # 一旦リリース + self.frame.release_tab() + # ファイルタブに移動 + self.frame.note_ctrl.ChangeSelection(self.frame.file_panel_ctrl.tab_idx) + # フォーム無効化 + self.frame.file_panel_ctrl.disable() + # タブ固定 + self.frame.file_panel_ctrl.fix_tab() + + # ファイルタブのコンソール + sys.stdout = self.frame.file_panel_ctrl.console_ctrl + + self.frame.elapsed_time = 0 + result = True + result = self.frame.is_valid() and result + + if not result: + # タブ移動可 + self.frame.release_tab() + # フォーム有効化 + self.frame.enable() + + return result + + # 読み込み開始 + if self.frame.load_worker: + logger.error("まだ処理が実行中です。終了してから再度実行してください。", decoration=MLogger.DECORATION_BOX) + else: + # 停止ボタンに切り替え + self.frame.file_panel_ctrl.check_btn_ctrl.SetLabel("読み込み処理停止") + self.frame.file_panel_ctrl.check_btn_ctrl.Enable() + + # 別スレッドで実行(次行がない場合、-1で終了フラグ) + self.frame.load_worker = LoadWorkerThread(self.frame, BulkLoadThreadEvent, row_no if row_no > line_idx else -1, True, False, False) + self.frame.load_worker.start() + + return result + + # 読み込み完了処理 + def on_load_result(self, event: wx.Event): + self.frame.elapsed_time = event.elapsed_time + + # タブ移動可 + self.frame.release_tab() + # フォーム有効化 + self.frame.enable() + # ワーカー終了 + self.frame.load_worker = None + # プログレス非表示 + self.frame.file_panel_ctrl.gauge_ctrl.SetValue(0) + + if not event.result: + # 終了音を鳴らす + self.frame.sound_finish() + + event.Skip() + return False + + result = self.frame.is_loaded_valid() + + if not result: + # タブ移動可 + self.frame.release_tab() + # フォーム有効化 + self.frame.enable() + + event.Skip() + return False + + logger.info("ファイルデータ読み込みが完了しました", decoration=MLogger.DECORATION_BOX, title="OK") + + # フォーム無効化 + self.frame.file_panel_ctrl.disable() + # タブ固定 + self.frame.file_panel_ctrl.fix_tab() + + if self.frame.worker: + logger.error("まだ処理が実行中です。終了してから再度実行してください。", decoration=MLogger.DECORATION_BOX) + else: + # 停止ボタンに切り替え + self.frame.file_panel_ctrl.exec_btn_ctrl.SetLabel("VMDサイジング停止") + self.frame.file_panel_ctrl.exec_btn_ctrl.Enable() + + # 別スレッドで実行 + self.frame.worker = SizingWorkerThread(self.frame, BulkSizingThreadEvent, event.target_idx, self.frame.is_saving, self.frame.is_out_log) + self.frame.worker.start() + + # スレッド実行結果 + def on_exec_result(self, event: wx.Event): + # 実行ボタンに切り替え + self.frame.file_panel_ctrl.exec_btn_ctrl.SetLabel("VMDサイジング実行") + self.frame.file_panel_ctrl.exec_btn_ctrl.Enable() + + if not event.result: + # 終了音を鳴らす + self.frame.sound_finish() + + event.Skip() + return False + + self.frame.elapsed_time += event.elapsed_time + worked_time = "\n処理時間: {0}".format(self.frame.show_worked_time()) + logger.info(worked_time) + + if self.frame.is_out_log and event.output_log_path and os.path.exists(event.output_log_path): + # ログ出力対象である場合、追記 + with open(event.output_log_path, mode='a', encoding='utf-8') as f: + f.write(worked_time) + + # ワーカー終了 + self.frame.worker = None + + if event.target_idx >= 0: + # 次のターゲットがある場合、次を処理 + logger.info("\n----------------------------------") + + return self.load(event, event.target_idx) + + # ファイルタブのコンソール + sys.stdout = self.frame.file_panel_ctrl.console_ctrl + + # 終了音を鳴らす + self.frame.sound_finish() + + # ファイルタブのコンソール + if sys.stdout != self.frame.file_panel_ctrl.console_ctrl: + sys.stdout = self.frame.file_panel_ctrl.console_ctrl + + # Bulk用データ消去 + self.frame.morph_panel_ctrl.bulk_morph_set_dict = {} + self.frame.arm_panel_ctrl.bulk_avoidance_set_dict = {} + self.frame.camera_panel_ctrl.bulk_camera_set_dict = {} + + # タブ移動可 + self.frame.release_tab() + # フォーム有効化 + self.frame.enable() + # プログレス非表示 + self.frame.file_panel_ctrl.gauge_ctrl.SetValue(0) + + logger.info("全てのサイジング処理が終了しました", decoration=MLogger.DECORATION_BOX, title="一括処理") + diff --git a/src/form/panel/CameraPanel.py b/src/form/panel/CameraPanel.py index 1fc57d2..eab915a 100644 --- a/src/form/panel/CameraPanel.py +++ b/src/form/panel/CameraPanel.py @@ -45,18 +45,18 @@ def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int): # カメラ距離調整スライダー self.camera_length_sizer = wx.BoxSizer(wx.HORIZONTAL) - self.camera_length_txt = wx.StaticText(self.header_panel, wx.ID_ANY, u"距離調整可動範囲", wx.DefaultPosition, wx.DefaultSize, 0) + self.camera_length_txt = wx.StaticText(self.header_panel, wx.ID_ANY, u"距離可動範囲", wx.DefaultPosition, wx.DefaultSize, 0) self.camera_length_txt.SetToolTip(u"ステージの大きさなどにより、カメラの距離の調整範囲を限定したい場合に\n" \ + "カメラの距離可動範囲を限定することができます。\n" \ + "可動範囲は手動で調整する事も可能です。") self.camera_length_txt.Wrap(-1) self.camera_length_sizer.Add(self.camera_length_txt, 0, wx.ALL, 5) - self.camera_length_type_ctrl = wx.Choice(self.header_panel, id=wx.ID_ANY, choices=["近距離", "中距離", "距離制限なし"]) + self.camera_length_type_ctrl = wx.Choice(self.header_panel, id=wx.ID_ANY, choices=["距離制限強", "距離制限弱", "距離制限なし"]) self.camera_length_type_ctrl.SetSelection(2) self.camera_length_type_ctrl.Bind(wx.EVT_CHOICE, self.on_camera_length_type) - self.camera_length_type_ctrl.SetToolTip(u"「近距離」 … 小さめのステージ用。距離可動範囲を厳しめに制限します。\n" \ - + "「中距離」 … 中くらいのステージ用。距離可動範囲を多少制限します。\n" \ + self.camera_length_type_ctrl.SetToolTip(u"「距離制限強」 … 小さめのステージ用。距離可動範囲を厳しめに制限します。\n" \ + + "「距離制限弱」 … 中くらいのステージ用。距離可動範囲を多少制限します。\n" \ + "「距離制限なし」 … 距離可動範囲を無制限とし、元モデルと同じ映り具合になるよう、最大限調整します。") self.camera_length_sizer.Add(self.camera_length_type_ctrl, 0, wx.ALL, 5) @@ -77,6 +77,8 @@ def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int): # カメラセット(key: ファイルセット番号, value: カメラセット) self.camera_set_dict = {} + # Bulk用カメラセット + self.bulk_camera_set_dict = {} # カメラセット用基本Sizer self.set_list_sizer = wx.BoxSizer(wx.VERTICAL) @@ -105,17 +107,10 @@ def set_output_vmd_path(self, event, is_force=False): # カメラ出力パスを強制的に変更する self.header_panel.set_output_vmd_path(event, True) - # カメラタブからカメラリスト生成 - def get_camera_list(self, set_no: int): - if set_no not in self.camera_set_dict: - # そもそも登録がなければ何もなし - return [] - else: - # あれば、そのNoのカメラリスト - return self.camera_set_dict[set_no].get_camera_list() - # カメラタブ初期化処理 def initialize(self, event: wx.Event): + self.bulk_camera_set_dict = {} + if 1 not in self.camera_set_dict: # 空から作る場合、ファイルタブのファイルセット参照 self.add_set(1, self.frame.file_panel_ctrl.file_set) diff --git a/src/form/panel/MorphPanel.py b/src/form/panel/MorphPanel.py index c5bcb83..0ced4f2 100644 --- a/src/form/panel/MorphPanel.py +++ b/src/form/panel/MorphPanel.py @@ -34,6 +34,8 @@ def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int): # モーフセット(key: ファイルセット番号, value: モーフセット) self.morph_set_dict = {} + # Bulk用モーフセット(key: ファイルセット番号, value: モーフlist) + self.bulk_morph_set_dict = {} # モーフセット用基本Sizer self.set_list_sizer = wx.BoxSizer(wx.VERTICAL) @@ -52,7 +54,10 @@ def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int): # モーフタブからモーフ置換リスト生成 def get_morph_list(self, set_no: int, vmd_digest: str, org_model_digest: str, rep_model_digest: str): - if set_no not in self.morph_set_dict: + if set_no in self.bulk_morph_set_dict: + # Bulk用のデータがある場合、優先取得 + return self.bulk_morph_set_dict[set_no], (len(self.bulk_morph_set_dict[set_no]) > 0) + elif set_no not in self.morph_set_dict: # そもそも登録がなければ何もなし return [], False else: @@ -67,6 +72,8 @@ def get_morph_list(self, set_no: int, vmd_digest: str, org_model_digest: str, re # モーフタブ初期化処理 def initialize(self, event: wx.Event): + self.bulk_morph_set_dict = {} + if 1 in self.morph_set_dict: # ファイルタブ用モーフのファイルセットがある場合 if self.frame.file_panel_ctrl.file_set.is_loaded(): diff --git a/src/service/SizingService.py b/src/service/SizingService.py index 5b82a20..2dbe70f 100644 --- a/src/service/SizingService.py +++ b/src/service/SizingService.py @@ -40,8 +40,8 @@ def execute(self): service_data_txt = "{service_data_txt}  変換先モデル: {replace_model} ({model_name})\n".format(service_data_txt=service_data_txt, replace_model=os.path.basename(data_set.rep_model.path), model_name=data_set.rep_model.name) # noqa if data_set.camera_org_model: - service_data_txt = "{service_data_txt}  カメラ作成元モデル: {trace_model} ({model_name})({offset_y})\n".format(service_data_txt=service_data_txt, - trace_model=os.path.basename(data_set.camera_org_model.path), model_name=data_set.camera_org_model.name, offset_y=data_set.camera_offset_y) # noqa + service_data_txt = "{service_data_txt}  カメラ作成元モデル: {trace_model} ({model_name})\n".format(service_data_txt=service_data_txt, + trace_model=os.path.basename(data_set.camera_org_model.path), model_name=data_set.camera_org_model.name) # noqa service_data_txt = "{service_data_txt}  Yオフセット: {camera_offset_y}\n".format(service_data_txt=service_data_txt, camera_offset_y=data_set.camera_offset_y) # noqa service_data_txt = "{service_data_txt}  スタンス追加補正有無: {detail_stance_flg}\n".format(service_data_txt=service_data_txt, diff --git a/src/service/parts/ArmAvoidanceService.pyx b/src/service/parts/ArmAvoidanceService.pyx index 2f0bfe2..0f70f6b 100644 --- a/src/service/parts/ArmAvoidanceService.pyx +++ b/src/service/parts/ArmAvoidanceService.pyx @@ -654,9 +654,9 @@ cdef class ArmAvoidanceService: if data_set_idx in self.options.arm_options.avoidance_target_list and "頭接触回避" in self.options.arm_options.avoidance_target_list[data_set_idx]: # 頭接触回避用剛体取得 head_rigidbody = data_set.rep_model.get_head_rigidbody() - head_rigidbody.is_small = (face_length <= 3) if head_rigidbody: + head_rigidbody.is_small = (face_length <= 3) logger.info("【No.%s - %s】頭接触回避用剛体: 半径: %s, 位置: %s", (data_set_idx + 1), direction, head_rigidbody.shape_size.x(), head_rigidbody.shape_position.to_log()) avoidance_links[head_rigidbody.name] = data_set.rep_model.create_link_2_top_one(data_set.rep_model.bone_indexes[head_rigidbody.bone_index]) avoidances[head_rigidbody.name] = head_rigidbody diff --git a/src/service/parts/CameraService.py b/src/service/parts/CameraService.py index da3bb80..45af5c8 100644 --- a/src/service/parts/CameraService.py +++ b/src/service/parts/CameraService.py @@ -681,8 +681,8 @@ def prepare_ratio(self, data_set_idx: int, org_model: PmxModel, rep_model: PmxMo # 顔の大きさ比率 head_ratio = rep_face_length / org_face_length - logger.info("【No.%s】作成元モデル 全長: %s, 頭身: %s, 顔の大きさ: %s", (data_set_idx + 1), org_total_height, org_heads, org_face_length) - logger.info("【No.%s】変換先モデル 全長: %s, 頭身: %s, 顔の大きさ: %s, Yオフセット: %s", (data_set_idx + 1), rep_total_height, rep_heads, rep_face_length, data_set.camera_offset_y) + logger.info("【No.%s】作成元モデル 全長: %s, 頭身: %s, 顔の大きさ: %s, Yオフセット: %s", (data_set_idx + 1), round(org_total_height, 5), round(org_heads, 5), round(org_face_length, 5), data_set.camera_offset_y) + logger.info("【No.%s】変換先モデル 全長: %s, 頭身: %s, 顔の大きさ: %s", (data_set_idx + 1), round(rep_total_height, 5), round(rep_heads, 5), round(rep_face_length, 5)) return org_total_height, org_face_length, org_heads, rep_total_height, rep_face_length, rep_heads, body_ratio, head_ratio diff --git a/src/utils/MFileutils.py b/src/utils/MFileutils.py index 5601ba7..5ed4de2 100644 --- a/src/utils/MFileutils.py +++ b/src/utils/MFileutils.py @@ -25,20 +25,20 @@ def resource_path(relative): # ファイル履歴読み込み def read_history(mydir_path): # ファイル履歴 - file_hitories = {"vmd": [], "org_pmx": [], "rep_pmx": [], "camera_vmd": [], "camera_pmx": [], "smooth_vmd": [], "smooth_pmx": [], "max": 50} + file_hitories = {"vmd": [], "org_pmx": [], "rep_pmx": [], "camera_vmd": [], "camera_pmx": [], "smooth_vmd": [], "smooth_pmx": [], "bulk_csv": [], "max": 50} # 履歴JSONファイルがあれば読み込み try: with open(os.path.join(mydir_path, 'history.json'), 'r') as f: file_hitories = json.load(f) # キーが揃っているかチェック - for key in ["vmd", "org_pmx", "rep_pmx", "camera_vmd", "camera_pmx", "smooth_pmx", "smooth_vmd"]: + for key in ["vmd", "org_pmx", "rep_pmx", "camera_vmd", "camera_pmx", "smooth_pmx", "smooth_vmd", "bulk_csv"]: if key not in file_hitories: file_hitories[key] = [] # 最大件数は常に上書き file_hitories["max"] = 50 except Exception: - file_hitories = {"vmd": [], "org_pmx": [], "rep_pmx": [], "camera_vmd": [], "camera_pmx": [], "smooth_vmd": [], "smooth_pmx": [], "max": 50} + file_hitories = {"vmd": [], "org_pmx": [], "rep_pmx": [], "camera_vmd": [], "camera_pmx": [], "smooth_vmd": [], "smooth_pmx": [], "bulk_csv": [], "max": 50} return file_hitories diff --git a/vmdising_np64.spec b/vmdising_np64.spec index b0fc597..fc85159 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,7 +26,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β59_64bit', + name='VmdSizing_5.01_β60_64bit', debug=False, bootloader_ignore_signals=False, strip=False, From 182948cece3cf4d8a5725ba0d112f4caa047cfdc Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Fri, 30 Oct 2020 04:06:46 +0900 Subject: [PATCH 27/37] =?UTF-8?q?5.01=5F=CE=B261?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VmdSizing_5.01_β61 miumiu ・ログ周り整理  ・ログの(xx%)表記でゼロ割が発生する可能性があるため、全部外部関数化 ・接触回避  ・頭剛体が作れなかった場合に、Noneオブジェクトにパラメータを設定しようとしていたのを修正  ・頭剛体が作れなかった場合の警告に、モデル名を出力し、囲みを追加 --- src/executor.py | 2 +- src/mmd/PmxReader.py | 1 + src/service/parts/ArmAlignmentService.pyx | 24 +++++----- src/service/parts/ArmAvoidanceService.pyx | 21 ++++++--- src/service/parts/StanceService.pyx | 57 ++++++++++++----------- src/utils/MLogger.py | 18 +++++++ vmdising_np64.spec | 2 +- 7 files changed, 78 insertions(+), 47 deletions(-) diff --git a/src/executor.py b/src/executor.py index 890b838..b898a82 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00_β60" +VERSION_NAME = "ver5.00_β61" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/mmd/PmxReader.py b/src/mmd/PmxReader.py index 7614988..64827fb 100644 --- a/src/mmd/PmxReader.py +++ b/src/mmd/PmxReader.py @@ -559,6 +559,7 @@ def read_data(self): pmx.bone_indexes[leg_center_bone.index] = leg_center_bone.name logger.test("len(bones): %s", len(pmx.bones)) + logger.test("bone_indexes: %s", pmx.bone_indexes.items()) logger.info("-- PMX ボーン読み込み完了") diff --git a/src/service/parts/ArmAlignmentService.pyx b/src/service/parts/ArmAlignmentService.pyx index c0cfa8f..0b4de07 100644 --- a/src/service/parts/ArmAlignmentService.pyx +++ b/src/service/parts/ArmAlignmentService.pyx @@ -231,10 +231,10 @@ cdef class ArmAlignmentService: all_org_global_effector_matrixs[fno][(data_set_idx, alignment_idx)] = org_global_matrixs[target_link.effector_bone_name] if fno // 200 > prev_block_fno: - logger.info("-- %sフレーム目:終了(%s%)【位置合わせ準備①】", fno, round((fno / fnos[-1]) * 100, 3)) + logger.count("位置合わせ準備①", fno, fnos) prev_block_fno = fno // 200 - logger.info("-- %sフレーム目:終了(%s%)【位置合わせ準備①】", fno, round((fno / fnos[-1]) * 100, 3)) + logger.count("位置合わせ準備①", fno, fnos) all_messages = {} all_is_alignment = {} @@ -314,10 +314,10 @@ cdef class ArmAlignmentService: all_is_alignment[(to_data_set_idx, to_alignment_idx)][fno] = is_alignment or all_is_alignment[(to_data_set_idx, to_alignment_idx)][fno] if fno // 500 > prev_block_fno: - logger.info("-- %sフレーム目:終了(%s%)【位置合わせ準備②】", fno, round((fno / fnos[-1]) * 100, 3)) + logger.count("位置合わせ準備②", fno, fnos) prev_block_fno = fno // 500 - logger.info("-- %sフレーム目:終了(%s%)【位置合わせ準備②】", fno, round((fno / fnos[-1]) * 100, 3)) + logger.count("位置合わせ準備②", fno, fnos) all_alignment_group_list = [] prev_block_fno = 0 @@ -466,10 +466,10 @@ cdef class ArmAlignmentService: break if fno // 500 > prev_block_fno: - logger.info("-- %sフレーム目:終了(%s%)【位置合わせ準備③】", fno, round((fno / fnos[-1]) * 100, 3)) + logger.count("位置合わせ準備③", fno, fnos) prev_block_fno = fno // 500 - - logger.info("-- %sフレーム目:終了(%s%)【位置合わせ準備③】", fno, round((fno / fnos[-1]) * 100, 3)) + + logger.count("位置合わせ準備③", fno, fnos) prev_block_fno = 0 @@ -571,10 +571,10 @@ cdef class ArmAlignmentService: # data_set.motion.bones[debug_bone_name][fno] = debug_bf if fno // 500 > prev_block_fno: - logger.info("-- %sフレーム目:終了(%s%)【位置合わせ準備④】", fno, round((fno / fnos[-1]) * 100, 3)) + logger.count("位置合わせ準備④", fno, fnos) prev_block_fno = fno // 500 - logger.info("-- %sフレーム目:終了(%s%)【位置合わせ準備④】", fno, round((fno / fnos[-1]) * 100, 3)) + logger.count("位置合わせ準備④", fno, fnos) return all_alignment_group_list, all_messages @@ -1259,10 +1259,10 @@ cdef class ArmAlignmentService: logger.info("○先端位置合わせ成功: f: %s(%s-%s)", fno, (data_set_idx + 1), target_link.effector_display_bone_name) if fno // 500 > prev_block_fno: - logger.info("-- %sフレーム目:終了(%s%)【位置合わせ】", fno, round((fno / fnos[-1]) * 100, 3)) + logger.count("位置合わせ", fno, fnos) prev_block_fno = fno // 500 - - logger.info("-- %sフレーム目:終了(%s%)【位置合わせ】", fno, round((fno / fnos[-1]) * 100, 3)) + + logger.count("位置合わせ", fno, fnos) # 手首位置合わせの準備 def prepare_wrist(self, data_set_idx: int): diff --git a/src/service/parts/ArmAvoidanceService.pyx b/src/service/parts/ArmAvoidanceService.pyx index 0f70f6b..5844aef 100644 --- a/src/service/parts/ArmAvoidanceService.pyx +++ b/src/service/parts/ArmAvoidanceService.pyx @@ -501,13 +501,15 @@ cdef class ArmAvoidanceService: # 全部成功している場合、成功ログ logger.info("○接触回避成功: f: %s(%s-%s)", fno, (data_set_idx + 1), arm_link.last_display_name()) - if fno // 500 > prev_block_fno and fnos[-1] > 0: - logger.info("-- %sフレーム目:終了(%s%)【No.%s - 接触回避 - %s】", fno, round((fno / fnos[-1]) * 100, 3), data_set_idx + 1, direction) + if fno // 500 > prev_block_fno: + logger.count("【No.{0} - 接触回避 - {1}】".format(data_set_idx + 1, direction), fno, fnos) prev_block_fno = fno // 500 # キーの登録が増えているかもなので、ここで取り直す fnos = data_set.motion.get_bone_fnos(*target_bone_names, start_fno=(fno + 1)) + logger.count("【No.{0} - 接触回避 - {1}】".format(data_set_idx + 1, direction), fno, fnos) + for bone_name in target_bone_names: # 非活性キー削除 data_set.motion.remove_unkey_bf(data_set_idx + 1, bone_name) @@ -583,10 +585,12 @@ cdef class ArmAvoidanceService: prev_collisions.append(collision) prev_collisions.append(near_collision) - if fno // 500 > prev_block_fno and fnos[-1] > 0: - logger.info("-- %sフレーム目:終了(%s%)【No.%s - 接触回避準備① - %s】", fno, round((fno / fnos[-1]) * 100, 3), data_set_idx + 1, direction) + if fno // 500 > prev_block_fno: + logger.count("【No.{0} - 接触回避準備① - {1}】".format(data_set_idx + 1, direction), fno, fnos) prev_block_fno = fno // 500 + logger.count("【No.{0} - 接触回避準備① - {1}】".format(data_set_idx + 1, direction), fno, fnos) + prev_block_fno = 0 all_avoidance_axis = {} for aidx, avoidance_list in enumerate(all_avoidance_list): @@ -615,9 +619,11 @@ cdef class ArmAvoidanceService: logger.debug("aidx: %s, d: %s, from: %s, to: %s, axis: %s, xd: %s, zd: %s", aidx, direction, from_fno, to_fno, all_avoidance_axis[from_fno], block_x_distance, block_z_distance) if fno // 1000 > prev_block_fno and fnos[-1] > 0: - logger.info("-- %sフレーム目:終了(%s%)【No.%s - 接触回避準備② - %s】", fno, round((fno / fnos[-1]) * 100, 3), data_set_idx + 1, direction) + logger.count("【No.{0} - 接触回避準備② - {1}】".format(data_set_idx + 1, direction), fno, fnos) prev_block_fno = fno // 1000 - + + logger.count("【No.{0} - 接触回避準備② - {1}】".format(data_set_idx + 1, direction), fno, fnos) + return all_avoidance_axis def calc_face_length(self, model: PmxModel): @@ -661,7 +667,8 @@ cdef class ArmAvoidanceService: avoidance_links[head_rigidbody.name] = data_set.rep_model.create_link_2_top_one(data_set.rep_model.bone_indexes[head_rigidbody.bone_index]) avoidances[head_rigidbody.name] = head_rigidbody else: - logger.info("【No.%s - %s】頭にウェイトが乗っている頂点が見つからなかった為、接触回避用剛体が作れませんでした。", (data_set_idx + 1), direction) + logger.warning("【No.%s - %s】\n「%s」で頭にウェイトが乗っている頂点が見つからなかった為、\n「頭接触回避」用剛体が作れませんでした。手動で剛体を設定してください。", \ + (data_set_idx + 1), direction, data_set.rep_model.name, decoration=MLogger.DECORATION_BOX) # self.calc_wrist_entity_vertex(data_set_idx, data_set.rep_model, "変換先", direction) # self.calc_elbow_entity_vertex(data_set_idx, data_set.rep_model, "変換先", direction) diff --git a/src/service/parts/StanceService.pyx b/src/service/parts/StanceService.pyx index 9ef1048..637ed06 100644 --- a/src/service/parts/StanceService.pyx +++ b/src/service/parts/StanceService.pyx @@ -469,10 +469,12 @@ cdef class StanceService(): bf = data_set.motion.calc_bf(bone_name, fno) data_set.motion.regist_bf(bf, bone_name, fno) - if fno // 2000 > prev_sep_fno and fnos[-1] > 0: - logger.info("-- %sフレーム目:終了(%s%)【No.%s - キーフレ追加 - %s】", fno, round((fno / fnos[-1]) * 100, 3), data_set_idx + 1, bone_name) + if fno // 2000 > prev_sep_fno: + logger.count("【No.{0} - キーフレ追加 - {1}】".format(data_set_idx + 1, bone_name), fno, fnos) prev_sep_fno = fno // 2000 + logger.count("【No.{0} - キーフレ追加 - {1}】".format(data_set_idx + 1, bone_name), fno, fnos) + if is_target_copy: prev_sep_fno = 0 for fno in fnos: @@ -484,10 +486,12 @@ cdef class StanceService(): data_set.motion.copy_interpolation(parent_bf, bf, MBezierUtils.BZ_TYPE_R) data_set.motion.regist_bf(bf, bone_name, fno, copy_interpolation=True) - if fno // 2000 > prev_sep_fno and fnos[-1] > 0: - logger.info("-- %sフレーム目:終了(%s%)【No.%s - 補間曲線設定 - %s】", fno, round((fno / fnos[-1]) * 100, 3), data_set_idx + 1, bone_name) + if fno // 2000 > prev_sep_fno: + logger.count("【No.{0} - 補間曲線設定 - {1}】".format(data_set_idx + 1, bone_name), fno, fnos) prev_sep_fno = fno // 2000 + logger.count("【No.{0} - 補間曲線設定 - {1}】".format(data_set_idx + 1, bone_name), fno, fnos) + return True except MKilledException as ke: raise ke @@ -647,8 +651,8 @@ cdef class StanceService(): else: logger.debug("○中間一致 f: %s, %s, twist_test_dot: %s, twist_test_x_dot: %s, twist_test_y_dot: %s", fno, arm_twist_bone_name, twist_test_dot, twist_test_x_dot, twist_test_y_dot) - if fno in log_target_idxs and last_fno > 0: - logger.info("-- %sフレーム目【No.%s - 中間捩り分散%s - %s】", fno, data_set_idx + 1, count, arm_twist_bone_name) + if fno in log_target_idxs: + logger.count("【No.{0} - 中間捩り分散{1} - {2}】".format(data_set_idx + 1, count, arm_twist_bone_name), fno, None, last_fno=last_fno) return True except MKilledException as ke: @@ -759,7 +763,7 @@ cdef class StanceService(): data_set.motion.bones[wrist_bone_name][fno] = wrist_bf if fno in log_target_idxs and last_fno > 0: - logger.info("-- %sフレーム目:終了(%s%)【No.%s - 捩り分散 - %s】", fno, round((fno / last_fno) * 100, 3), data_set_idx + 1, arm_twist_bone_name) + logger.count("【No.{0} - 捩り分散 - {1}】".format(data_set_idx + 1, arm_twist_bone_name), fno, None, last_fno=last_fno) return True except MKilledException as ke: @@ -1619,9 +1623,10 @@ cdef class StanceService(): data_set.motion.regist_bf(ik_bf, target_bone_name, fno) - if fno // 500 > prev_sep_fno and fnos[-1] > 0: - logger.info("-- %sフレーム目:終了(%s%)【No.%s - %s補正】", fno, round((fno / fnos[-1]) * 100, 3), data_set_idx + 1, target_bone_name) + if fno // 500 > prev_sep_fno: + logger.count("【No.{0} - {1}補正】".format(data_set_idx + 1, target_bone_name), fno, fnos) prev_sep_fno = fno // 500 + logger.info("%s足IK補正:終了【No.%s】", direction, (data_set_idx + 1)) else: @@ -1833,8 +1838,8 @@ cdef class StanceService(): data_set.motion.regist_bf(toe_ik_bf, toe_ik_bone_name, fno) data_set.motion.regist_bf(leg_ik_bf, leg_ik_bone_name, fno) - if fno // 500 > prev_sep_fno and fnos[-1] > 0: - logger.info("-- %sフレーム目:終了(%s%)【No.%s - %s補正】", fno, round((fno / fnos[-1]) * 100, 3), data_set_idx + 1, toe_ik_bone_name) + if fno // 500 > prev_sep_fno: + logger.count("【No.{0} - {1}補正】".format(data_set_idx + 1, toe_ik_bone_name), fno, fnos) prev_sep_fno = fno // 500 # if is_execed_toe_ik: @@ -1970,8 +1975,8 @@ cdef class StanceService(): # つま先補正なし pass - if fno // 500 > prev_sep_fno and fnos[-1] > 0: - logger.info("-- %sフレーム目:終了(%s%)【No.%s - %sつま先補正】", fno, round((fno / fnos[-1]) * 100, 3), data_set_idx + 1, direction) + if fno // 500 > prev_sep_fno: + logger.count("【No.{0} - {1}つま先補正】".format(data_set_idx + 1, direction), fno, fnos) prev_sep_fno = fno // 500 logger.info("%sつま先補正:終了【No.%s】", direction, (data_set_idx + 1)) @@ -2059,8 +2064,8 @@ cdef class StanceService(): org_center_bone_name, rep_center_bone_name) logger.debug("f: %s, 体幹オフセット後: %s", bf.fno, bf.position) - if fno // 500 > prev_fno and fnos[-1] > 0: - logger.info("-- %sフレーム目:終了(%s%)【No.%s - センターXZ補正】", fno, round((fno / fnos[-1]) * 100, 3), data_set_idx + 1) + if fno // 500 > prev_fno: + logger.count("【No.{0} - センターXZ補正】".format(data_set_idx + 1), fno, fnos) prev_fno = fno // 500 logger.info("センターXZ補正: 終了【No.%s】", (data_set_idx + 1)) @@ -2156,7 +2161,7 @@ cdef class StanceService(): data_set.motion.regist_bf(bf, bf.name, fno) if fno // 500 > prev_fno and fnos[-1] > 0: - logger.info("-- %sフレーム目:終了(%s%)【No.%s - センターY補正】", fno, round((fno / fnos[-1]) * 100, 3), data_set_idx + 1) + logger.count("【No.{0} - センターY補正】".format(data_set_idx + 1), fno, fnos) prev_fno = fno // 500 logger.info("センターY補正: 終了【No.%s】", (data_set_idx + 1)) @@ -2548,8 +2553,8 @@ cdef class StanceService(): # bf登録 data_set.motion.regist_bf(upper_bf, "上半身", fno) - if fno // 500 > prev_fno and fnos[-1] > 0: - logger.info("-- %sフレーム目:終了(%s%)【No.%s - 上半身補正】", fno, round((fno / fnos[-1]) * 100, 3), data_set_idx + 1) + if fno // 500 > prev_fno: + logger.count("【No.{0} - 上半身補正】".format(data_set_idx + 1), fno, fnos) prev_fno = fno // 500 # 子の角度調整 @@ -2638,8 +2643,8 @@ cdef class StanceService(): # bf登録 data_set.motion.regist_bf(upper2_bf, "上半身2", fno) - if fno // 500 > prev_fno and fnos[-1] > 0: - logger.info("-- %sフレーム目:終了(%s%)【No.%s - 上半身2補正】", fno, round((fno / fnos[-1]) * 100, 3), data_set_idx + 1) + if fno // 500 > prev_fno: + logger.count("【No.{0} - 上半身2補正】".format(data_set_idx + 1), fno, fnos) prev_fno = fno // 500 # 子の角度調整 @@ -2772,8 +2777,8 @@ cdef class StanceService(): # bf登録 data_set.motion.regist_bf(lower_bf, "下半身", fno) - if fno // 500 > prev_fno and fnos[-1] > 0: - logger.info("-- %sフレーム目:終了(%s%)【No.%s - 下半身補正】", fno, round((fno / fnos[-1]) * 100, 3), data_set_idx + 1) + if fno // 500 > prev_fno: + logger.count("【No.{0} - 下半身補正】".format(data_set_idx + 1), fno, fnos) prev_fno = fno // 500 # 子の角度調整 @@ -3136,8 +3141,8 @@ cdef class StanceService(): # bf登録 data_set.motion.regist_bf(bf, shoulder_name, bf.fno) - if fno // 500 > prev_fno and fnos[-1] > 0: - logger.info("-- %sフレーム目:終了(%s%)【No.%s - %sスタンス補正】", fno, round((fno / fnos[-1]) * 100, 3), data_set_idx + 1, shoulder_name) + if fno // 500 > prev_fno: + logger.count("【No.{0} - {1}スタンス補正】".format(data_set_idx + 1, shoulder_name), fno, fnos) prev_fno = fno // 500 # 子の角度調整 @@ -3253,8 +3258,8 @@ cdef class StanceService(): # bf登録 data_set.motion.regist_bf(shoulder_bf, shoulder_name, fno) - if fno // 500 > prev_fno and fnos[-1] > 0: - logger.info("-- %sフレーム目:終了(%s%)【No.%s - %sスタンス補正】", fno, round((fno / fnos[-1]) * 100, 3), data_set_idx + 1, shoulder_name) + if fno // 500 > prev_fno: + logger.count("【No.{0} - {1}スタンス補正】".format(data_set_idx + 1, shoulder_name), fno, fnos) prev_fno = fno // 500 # 子の角度調整 diff --git a/src/utils/MLogger.py b/src/utils/MLogger.py index 47569b4..7fb33d3 100644 --- a/src/utils/MLogger.py +++ b/src/utils/MLogger.py @@ -99,6 +99,24 @@ def info(self, msg, *args, **kwargs): kwargs["level"] = logging.INFO self.print_logger(msg, *args, **kwargs) + # ログレベルカウント + def count(self, msg, fno, fnos, *args, **kwargs): + last_fno = 0 + + if fnos and len(fnos) > 0 and fnos[-1] > 0: + last_fno = fnos[-1] + + if not fnos and kwargs and "last_fno" in kwargs and kwargs["last_fno"] > 0: + last_fno = kwargs["last_fno"] + + if last_fno > 0: + if not kwargs: + kwargs = {} + + kwargs["level"] = logging.INFO + log_msg = "-- {0}フレーム目:終了({1}%){2}".format(fno, round((fno / last_fno) * 100, 3), msg) + self.print_logger(log_msg, *args, **kwargs) + def warning(self, msg, *args, **kwargs): if not kwargs: kwargs = {} diff --git a/vmdising_np64.spec b/vmdising_np64.spec index fc85159..53f765f 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,7 +26,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β60_64bit', + name='VmdSizing_5.01_β61_64bit', debug=False, bootloader_ignore_signals=False, strip=False, From 94a6762962b85520e7a58127db72b2976270e892 Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Sat, 31 Oct 2020 12:50:58 +0900 Subject: [PATCH 28/37] =?UTF-8?q?5.01=5F=CE=B262?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VmdSizing_5.01_β62 miumiu ・サイジング  ・モーフ置換データを冒頭ログに出力追加 ・一括サイジング  ・CSV読み込み確認ボタン追加  ・確認ボタン押下時に、サイジング冒頭と同等のログを出力するよう処理追加  ・出力パス設定欄削除  ・複数人モーションの場合、指位置合わせを強制的に0に設定  ・サンプル.csv調整 --- archive/Readme.txt | 11 +- ...4\347\224\250\351\235\231\347\224\273.url" | 2 + ...5\343\203\263\343\203\227\343\203\253.csv" | 12 +- ...4\343\203\236\343\203\203\343\203\210.csv" | 2 - ...\343\203\236\343\203\203\343\203\2102.csv" | 6 - src/executor.py | 2 +- src/form/panel/BulkPanel.py | 274 +++++++++++++----- src/mmd/VmdData.pyx | 6 +- src/service/SizingService.py | 7 + vmdising_np64.spec | 2 +- 10 files changed, 229 insertions(+), 95 deletions(-) create mode 100644 "archive/\343\202\263\343\203\263\343\203\206\343\203\263\343\203\204\343\203\204\343\203\252\343\203\274\347\224\250\351\235\231\347\224\273.url" delete mode 100644 "archive/\344\270\200\346\213\254\345\207\246\347\220\206\347\224\250\343\203\225\343\202\251\343\203\274\343\203\236\343\203\203\343\203\210.csv" delete mode 100644 "archive/\344\270\200\346\213\254\345\207\246\347\220\206\347\224\250\343\203\225\343\202\251\343\203\274\343\203\236\343\203\203\343\203\2102.csv" diff --git a/archive/Readme.txt b/archive/Readme.txt index 8fc9e21..fd7673a 100644 --- a/archive/Readme.txt +++ b/archive/Readme.txt @@ -208,6 +208,10 @@ A ̓IȒĺuꊇTCWOTv.csvvmFB wb_̈sڂ͓ǂݔ΂܂B +LibreOfficeŊJۂ̒ӓ_ +@EeLXg̃C|[g_CAOŁA؂̃IvVuZ~RvOĂ + + 01 c O[vNo TCWOsO[vPʂŘAԂ‚ĂB 02 c Ώۃ[VVMD/VPD(tpX) @@ -237,15 +241,18 @@ A 14 c 蕪U ut@Cvuv^úu蕪Uv`FbN{bNXB0:A1:L̂ꂩK{łB 15 c [tu - u[tv^úu[tuṽZbgBu[t:u惂[t:傫;1‚̃ZbgƂĂB + u[tv^úu[tuṽZbgBuu[t:u惂[t:傫;v1‚̃ZbgƂĂB + iŌ̃Z~R͕K{łj 16 c ڐG urv^úuڐGv`FbN{bNXB0:A1:L̂ꂩK{łB 17 c ڐG - urv^úuڐGv̑Ώۍ̖XgB̖;̂悤ɔpZ~RŌqłB + urv^úuڐGv̑Ώۍ̖XgBu̖;v̂悤ɔpZ~RŌqłB + iŌ̃Z~R͕K{łj 18 c ʒu킹 urv^úuʒu킹v`FbN{bNXB0:A1:L̂ꂩK{łB 19 c wʒu킹 urv^úuẅʒuňʒu킹sv`FbN{bNXB0:A1:L̂ꂩK{łB + l[V̏ꍇAI0ɂȂ܂B 20 c ʒu킹 urv^úuƂ̈ʒu킹ꏏɍsv`FbN{bNXB0:A1:L̂ꂩK{łB 21 c ̋ diff --git "a/archive/\343\202\263\343\203\263\343\203\206\343\203\263\343\203\204\343\203\204\343\203\252\343\203\274\347\224\250\351\235\231\347\224\273.url" "b/archive/\343\202\263\343\203\263\343\203\206\343\203\263\343\203\204\343\203\204\343\203\252\343\203\274\347\224\250\351\235\231\347\224\273.url" new file mode 100644 index 0000000..5cc784b --- /dev/null +++ "b/archive/\343\202\263\343\203\263\343\203\206\343\203\263\343\203\204\343\203\204\343\203\252\343\203\274\347\224\250\351\235\231\347\224\273.url" @@ -0,0 +1,2 @@ +[InternetShortcut] + URL=https://seiga.nicovideo.jp/seiga/im9755721 \ No newline at end of file diff --git "a/archive/\344\270\200\346\213\254\343\202\265\343\202\244\343\202\270\343\203\263\343\202\260\343\202\265\343\203\263\343\203\227\343\203\253.csv" "b/archive/\344\270\200\346\213\254\343\202\265\343\202\244\343\202\270\343\203\263\343\202\260\343\202\265\343\203\263\343\203\227\343\203\253.csv" index 2826bbd..0474055 100644 --- "a/archive/\344\270\200\346\213\254\343\202\265\343\202\244\343\202\270\343\203\263\343\202\260\343\202\265\343\203\263\343\203\227\343\203\253.csv" +++ "b/archive/\344\270\200\346\213\254\343\202\265\343\202\244\343\202\270\343\203\263\343\202\260\343\202\265\343\203\263\343\203\227\343\203\253.csv" @@ -1,6 +1,6 @@ -O[vNo(l[V͓No),Ώۃ[VVMD/VPD(tpX),[V쐬fPMX(tpX),[Vϊ惂fPMX(tpX),oVMD(tpXA󗓃ftHݒ),Z^[XZ␳(0:A1:L),㔼g␳(0:A1:L),g␳(0:A1:L),hj␳(0:A1:L),‚ܐ␳(0:A1:L),‚ܐhj␳(0:A1:L),␳(0:A1:L),Z^[Y␳(0:A1:L),蕪U(0:ȂA1:),[tu(::傫;),ڐG(0:ȂA1:),ڐG(̖;),ʒu킹(0:ȂA1:),wʒu킹(0:ȂA1:),ʒu킹(0:ȂA1:),̋,w̋,Ƃ̋,r`FbNXLbv(0:ȂA1:),J[VVMD(tpXAO[v1ڂ̂),o̓JVMD(tpXA󗓃ftHݒ),“͈,J쐬fPMX(tpX),SYItZbg -1,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_1l\h}cM[ motion zzp moka\h}cM[_0-500.vmd,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\VOCALOID\~N\TdaftH~N_ver1.1 q\Tda~N_ftHver.pmx,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\\039_OclY\OclY |G v1.35\OclY_ʏ_W.pmx,,1,0,0,0,0,0,0,0,0,΂:ɂ:1;,0,,1,1,1,1.7,1.4,1.2,0,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_1l\h}cM[J 䂫˂\JWiTdaj_0-515.vmd,,5,,0 -2,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_1l\tIII Ȃ‚\nac_aikotoba3_0-500.vmd,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\VOCALOID\~N\Ԏ~N\Ԏ~N_W.pmx,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\cCXe\04_XJrA\J2007 ؂ȊP\ʏ탂f\J.pmx,,0,0,0,0,0,0,0,0,0,,0,,0,0,0,1.7,1.4,1.2,1,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_1l\tIII Ȃ‚\J[N.vmd,,5,,0 -3,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_2l\jA_vmd P.I.P\ėp_ȉ\jA_ėp_1377-1498.vmd,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\VOCALOID\~N\Tac~Nzzp\Tac~NVer1.3.1\ver1.3.1_m[}\Tac~Nver1.3.1im[}j.pmx,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\cCXe\06_COjnCh\CfAEVEh Ȃ\ȂCfA_20200813.pmx,,0,0,1,0,0,0,0,0,1,,0,,1,0,1,1.7,1.4,1.2,0,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_2l\jA_bv\Jzz_bym\ėp_ȉp\ȉpJ_0-1000.vmd,,5,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\VOCALOID\~N\߂ڂ ~N Ver1.11\߂ڂ ~N ver1.11.pmx,0 -3,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_2l\jA_vmd P.I.P\ėp_ȉ\jA_ėp_1377-1498.vmd,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\VOCALOID\~N\Tda~Ns[X\Tda~Ns[XRSP.pmx,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\cCXe\06_COjnCh\IgEVEh Im{\IgEVEh @A[L^CvEMA.pmx,,0,0,0,1,0,0,0,0,1,,0,,1,0,1,1.7,1.4,1.2,0,,,5,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\VOCALOID\~N\߂ڂ ~N Ver1.11\߂ڂ ~N ver1.11.pmx,0 -4,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_1l\ELECT yurie\GNg_2375-2420.vmd,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\Q[\퍑BASARA\K ʂ ver.1.24\^cKvߑ1.24yʔ.pmx,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\͑ꂭ\ۂՒav.2\ۂՒa_v.2_3.pmx,,0,1,0,0,0,0,0,0,0,,1,ڐG;ڐG,0,0,0,1.7,1.4,1.2,0,,,,, +O[vNo(l[V͓No),Ώۃ[VVMD/VPD(tpX),[V쐬fPMX(tpX),[Vϊ惂fPMX(tpX),Z^[XZ␳(0:A1:L),㔼g␳(0:A1:L),g␳(0:A1:L),hj␳(0:A1:L),‚ܐ␳(0:A1:L),‚ܐhj␳(0:A1:L),␳(0:A1:L),Z^[Y␳(0:A1:L),蕪U(0:ȂA1:),[tu(::傫;),ڐG(0:ȂA1:),ڐG(̖;),ʒu킹(0:ȂA1:),wʒu킹(0:ȂA1:),ʒu킹(0:ȂA1:),̋,w̋,Ƃ̋,r`FbNXLbv(0:ȂA1:),J[VVMD(tpXAO[v1ڂ̂),“͈,J쐬fPMX(tpX),SYItZbg +1,D:\MMD\h}cM[ moka\h}cM[.vmd,D:\MMD\Tda~N_ftHver.pmx,D:\MMD\OclY.pmx,1,1,1,1,1,1,1,1,1,,0,,1,1,1,1.7,1.4,1.2,0,D:\MMD\h}cM[J 䂫˂\JWiTdaj.vmd,5,,0 +2,D:\MMD\tIII Ȃ‚\nac_aikotoba3.vmd,D:\MMD\Ԏ~N\Ԏ~N.pmx,D:\MMD\J.pmx,1,1,1,1,1,1,1,1,1,^::1;:rbN:1;::0.8;,0,,0,0,0,1.7,1.4,1.2,1,D:\MMD\tIII Ȃ‚\J[N.vmd,5,,0 +3,D:\MMD\jA_vmd P.I.P\ėp_ȉ\jA_ėp.vmd,D:\MMD\Tac~Nver1.3.1im[}j.pmx,D:\MMD\ȂCfA.pmx,1,1,1,1,1,1,1,1,0,,0,,1,0,1,2.5,1.4,1.2,0,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_2l\jA_bv\Jzz_bym\ėp_ȉp\ȉpJ.vmd,5,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\VOCALOID\~N\߂ڂ ~N Ver1.11\߂ڂ ~N ver1.11.pmx,0 +3,D:\MMD\jA_vmd P.I.P\ėp_ȉ\jA_ėp.vmd,D:\MMD\Tda~Ns[XRSP.pmx,D:\MMD\IgEVEh @A[L^CvEMA.pmx,1,1,1,1,1,1,1,1,0,,0,,1,0,1,2.5,1.4,1.2,0,,5,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\VOCALOID\~N\߂ڂ ~N Ver1.11\߂ڂ ~N ver1.11.pmx,0 +4,D:\MMD\ELECT yurie\GNg.vmd,D:\MMD\^cKvߑ1.24yʔ.pmx,D:\MMD\ۂՒa_v.2.pmx,1,1,1,1,1,1,1,1,1,,1,ڐG2;ڐG;ڐG;,1,1,1,1.7,1.4,1.2,0,,,, diff --git "a/archive/\344\270\200\346\213\254\345\207\246\347\220\206\347\224\250\343\203\225\343\202\251\343\203\274\343\203\236\343\203\203\343\203\210.csv" "b/archive/\344\270\200\346\213\254\345\207\246\347\220\206\347\224\250\343\203\225\343\202\251\343\203\274\343\203\236\343\203\203\343\203\210.csv" deleted file mode 100644 index 10086b3..0000000 --- "a/archive/\344\270\200\346\213\254\345\207\246\347\220\206\347\224\250\343\203\225\343\202\251\343\203\274\343\203\236\343\203\203\343\203\210.csv" +++ /dev/null @@ -1,2 +0,0 @@ -O[vNo(l[V͓No),Ώۃ[VVMD/VPD(tpX),[V쐬fPMX(tpX),[Vϊ惂fPMX(tpX),oVMD(tpXA󗓃ftHݒ),Z^[XZ␳(0:A1:L),㔼g␳(0:A1:L),g␳(0:A1:L),hj␳(0:A1:L),‚ܐ␳(0:A1:L),‚ܐhj␳(0:A1:L),␳(0:A1:L),Z^[Y␳(0:A1:L),蕪U(0:ȂA1:),r`FbNXLbv(0:ȂA1:),[tu(::傫;),ڐG(0:ȂA1:),ڐG(A;B),ʒu킹(0:ȂA1:),wʒu킹(0:ȂA1:),ʒu킹(0:ȂA1:),̋,w̋,̋,J[V(tpXAO[v1ڂ̂),o̓JVMD(tpXA󗓃ftHݒ),ғ͈,J쐬fPMX(tpX),SYItZbg -1,D:\MMD\UserFile\Model\[V.vmd,D:\MMD\UserFile\Model\~NVer2 W.pmx,D:\MMD\UserFile\Model\MEIKO.pmx,,1,1,1,1,1,1,1,1,1,0,"΂:ɂ:1,:߂:0.8",1,"ڐG,",1,1,1,1.7,1.4,1.2,D:\MMD\UserFile\Model\J.vmd,,5,D:\MMD\UserFile\Model\.pmx,0 diff --git "a/archive/\344\270\200\346\213\254\345\207\246\347\220\206\347\224\250\343\203\225\343\202\251\343\203\274\343\203\236\343\203\203\343\203\2102.csv" "b/archive/\344\270\200\346\213\254\345\207\246\347\220\206\347\224\250\343\203\225\343\202\251\343\203\274\343\203\236\343\203\203\343\203\2102.csv" deleted file mode 100644 index 2826bbd..0000000 --- "a/archive/\344\270\200\346\213\254\345\207\246\347\220\206\347\224\250\343\203\225\343\202\251\343\203\274\343\203\236\343\203\203\343\203\2102.csv" +++ /dev/null @@ -1,6 +0,0 @@ -O[vNo(l[V͓No),Ώۃ[VVMD/VPD(tpX),[V쐬fPMX(tpX),[Vϊ惂fPMX(tpX),oVMD(tpXA󗓃ftHݒ),Z^[XZ␳(0:A1:L),㔼g␳(0:A1:L),g␳(0:A1:L),hj␳(0:A1:L),‚ܐ␳(0:A1:L),‚ܐhj␳(0:A1:L),␳(0:A1:L),Z^[Y␳(0:A1:L),蕪U(0:ȂA1:),[tu(::傫;),ڐG(0:ȂA1:),ڐG(̖;),ʒu킹(0:ȂA1:),wʒu킹(0:ȂA1:),ʒu킹(0:ȂA1:),̋,w̋,Ƃ̋,r`FbNXLbv(0:ȂA1:),J[VVMD(tpXAO[v1ڂ̂),o̓JVMD(tpXA󗓃ftHݒ),“͈,J쐬fPMX(tpX),SYItZbg -1,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_1l\h}cM[ motion zzp moka\h}cM[_0-500.vmd,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\VOCALOID\~N\TdaftH~N_ver1.1 q\Tda~N_ftHver.pmx,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\\039_OclY\OclY |G v1.35\OclY_ʏ_W.pmx,,1,0,0,0,0,0,0,0,0,΂:ɂ:1;,0,,1,1,1,1.7,1.4,1.2,0,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_1l\h}cM[J 䂫˂\JWiTdaj_0-515.vmd,,5,,0 -2,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_1l\tIII Ȃ‚\nac_aikotoba3_0-500.vmd,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\VOCALOID\~N\Ԏ~N\Ԏ~N_W.pmx,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\cCXe\04_XJrA\J2007 ؂ȊP\ʏ탂f\J.pmx,,0,0,0,0,0,0,0,0,0,,0,,0,0,0,1.7,1.4,1.2,1,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_1l\tIII Ȃ‚\J[N.vmd,,5,,0 -3,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_2l\jA_vmd P.I.P\ėp_ȉ\jA_ėp_1377-1498.vmd,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\VOCALOID\~N\Tac~Nzzp\Tac~NVer1.3.1\ver1.3.1_m[}\Tac~Nver1.3.1im[}j.pmx,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\cCXe\06_COjnCh\CfAEVEh Ȃ\ȂCfA_20200813.pmx,,0,0,1,0,0,0,0,0,1,,0,,1,0,1,1.7,1.4,1.2,0,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_2l\jA_bv\Jzz_bym\ėp_ȉp\ȉpJ_0-1000.vmd,,5,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\VOCALOID\~N\߂ڂ ~N Ver1.11\߂ڂ ~N ver1.11.pmx,0 -3,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_2l\jA_vmd P.I.P\ėp_ȉ\jA_ėp_1377-1498.vmd,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\VOCALOID\~N\Tda~Ns[X\Tda~Ns[XRSP.pmx,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\cCXe\06_COjnCh\IgEVEh Im{\IgEVEh @A[L^CvEMA.pmx,,0,0,0,1,0,0,0,0,1,,0,,1,0,1,1.7,1.4,1.2,0,,,5,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\VOCALOID\~N\߂ڂ ~N Ver1.11\߂ڂ ~N ver1.11.pmx,0 -4,D:\MMD\MikuMikuDance_v926x64\UserFile\Motion\_X_1l\ELECT yurie\GNg_2375-2420.vmd,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\Q[\퍑BASARA\K ʂ ver.1.24\^cKvߑ1.24yʔ.pmx,D:\MMD\MikuMikuDance_v926x64\UserFile\Model\͑ꂭ\ۂՒav.2\ۂՒa_v.2_3.pmx,,0,1,0,0,0,0,0,0,0,,1,ڐG;ڐG,0,0,0,1.7,1.4,1.2,0,,,,, diff --git a/src/executor.py b/src/executor.py index b898a82..c95838a 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00_β61" +VERSION_NAME = "ver5.00_β62" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/form/panel/BulkPanel.py b/src/form/panel/BulkPanel.py index fc8eda7..f1e130f 100644 --- a/src/form/panel/BulkPanel.py +++ b/src/form/panel/BulkPanel.py @@ -12,6 +12,7 @@ from form.parts.ConsoleCtrl import ConsoleCtrl from form.worker.SizingWorkerThread import SizingWorkerThread from form.worker.LoadWorkerThread import LoadWorkerThread +from module.MOptions import MOptions, MOptionsDataSet, MArmProcessOptions from utils import MFormUtils, MFileUtils # noqa from utils.MLogger import MLogger # noqa @@ -43,6 +44,13 @@ def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int): btn_sizer = wx.BoxSizer(wx.HORIZONTAL) + # 一括サイジング確認ボタン + self.check_btn_ctrl = wx.Button(self, wx.ID_ANY, u"一括サイジング確認", wx.DefaultPosition, wx.Size(200, 50), 0) + self.check_btn_ctrl.SetToolTip(u"指定されたCSVデータの設定を確認します。") + self.check_btn_ctrl.Bind(wx.EVT_LEFT_DCLICK, self.on_doubleclick) + self.check_btn_ctrl.Bind(wx.EVT_LEFT_DOWN, self.on_check_click) + btn_sizer.Add(self.check_btn_ctrl, 0, wx.ALL, 5) + # 一括サイジング実行ボタン self.bulk_btn_ctrl = wx.Button(self, wx.ID_ANY, u"一括サイジング実行", wx.DefaultPosition, wx.Size(200, 50), 0) self.bulk_btn_ctrl.SetToolTip(u"一括でサイジングを実行します") @@ -74,11 +82,13 @@ def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int): def disable(self): self.bulk_csv_file_ctrl.disable() self.bulk_btn_ctrl.Disable() + self.check_btn_ctrl.Disable() # フォーム無効化 def enable(self): self.bulk_csv_file_ctrl.enable() self.bulk_btn_ctrl.Enable() + self.check_btn_ctrl.Enable() def on_doubleclick(self, event: wx.Event): self.timer.Stop() @@ -130,15 +140,32 @@ def on_bulk(self, event: wx.Event): self.save() # サイジング可否チェックの後に実行 - self.check(event) + self.check(event, True) event.Skip() else: logger.error("まだ処理が実行中です。終了してから再度実行してください。", decoration=MLogger.DECORATION_BOX) event.Skip(False) - def save(self): + def on_check_click(self, event: wx.Event): + self.timer = wx.Timer(self, TIMER_ID) + self.timer.Start(200) + self.Bind(wx.EVT_TIMER, self.on_check, id=TIMER_ID) + + # サイジング一括確認 + def on_check(self, event: wx.Event): + if self.timer: + self.timer.Stop() + self.Unbind(wx.EVT_TIMER, id=TIMER_ID) + + # 出力先をファイルパネルのコンソールに変更 + sys.stdout = self.console_ctrl + # サイジング可否チェックのみ + self.check(event, False) + return + + def save(self): # 履歴保持 self.bulk_csv_file_ctrl.save() @@ -146,7 +173,7 @@ def save(self): MFileUtils.save_history(self.frame.mydir_path, self.frame.file_hitories) # データチェック - def check(self, event: wx.Event): + def check(self, event: wx.Event, is_exec: bool): # フォーム無効化 self.disable() # タブ固定 @@ -157,56 +184,151 @@ def check(self, event: wx.Event): reader = csv.reader(f) next(reader) # ヘッダーを読み飛ばす + prev_group_no = -1 + now_model_no = -1 + service_data_txt = "" for ridx, rows in enumerate(reader): row_no = ridx group_no_result, group_no = self.read_csv_row(rows, row_no, 0, "グループNo", True, int, r"\d+", "数値のみ", None) - org_motion_result, org_motion_path = self.read_csv_row(rows, row_no, 1, "調整対象モーションVMD/VPD", True, str, None, None, ("vmd", "vpd")) - org_model_result, org_model_path = self.read_csv_row(rows, row_no, 2, "モーション作成元モデルPMX", True, str, None, None, ("pmx")) - rep_model_result, rep_model_path = self.read_csv_row(rows, row_no, 3, "モーション変換先モデルPMX", True, str, None, None, ("pmx")) - output_motion_result, output_motion_path = self.read_csv_row(rows, row_no, 4, "出力VMD", False, str, None, None, ("vmd")) - stance_center_xz_result, stance_center_xz_datas = self.read_csv_row(rows, row_no, 5, "センターXZ補正", True, int, r"(0|1)", "0 もしくは 1", None) - stance_upper_result, stance_upper_datas = self.read_csv_row(rows, row_no, 6, "上半身補正", True, int, r"(0|1)", "0 もしくは 1", None) - stance_lower_result, stance_lower_datas = self.read_csv_row(rows, row_no, 7, "下半身補正", True, int, r"(0|1)", "0 もしくは 1", None) - stance_leg_ik_result, stance_leg_ik_datas = self.read_csv_row(rows, row_no, 8, "足IK補正", True, int, r"(0|1)", "0 もしくは 1", None) - stance_toe_result, stance_toe_datas = self.read_csv_row(rows, row_no, 9, "つま先補正", True, int, r"(0|1)", "0 もしくは 1", None) - stance_toe_ik_result, stance_toe_ik_datas = self.read_csv_row(rows, row_no, 10, "つま先IK補正", True, int, r"(0|1)", "0 もしくは 1", None) - stance_shoulder_result, stance_shoulder_datas = self.read_csv_row(rows, row_no, 11, "肩補正", True, int, r"(0|1)", "0 もしくは 1", None) - stance_center_y_result, stance_center_y_datas = self.read_csv_row(rows, row_no, 12, "センターY補正", True, int, r"(0|1)", "0 もしくは 1", None) - separate_twist_result, separate_twist_datas = self.read_csv_row(rows, row_no, 13, "捩り分散", True, int, r"(0|1)", "0 もしくは 1", None) - morph_result, morph_datas = self.read_csv_row(rows, row_no, 14, "モーフ置換", False, str, r"[^\:]+\:[^\:]+\:\d+\.?\d*\;", "元:先:大きさ;", None) - arm_avoidance_result, arm_avoidance_datas = self.read_csv_row(rows, row_no, 15, "接触回避", True, int, r"(0|1)", "0 もしくは 1", None) - avoidance_name_result, avoidance_name_datas = self.read_csv_row(rows, row_no, 16, "接触回避剛体", False, str, r"[^\;]+\;", "剛体名;", None) - arm_alignment_result, arm_alignment_datas = self.read_csv_row(rows, row_no, 17, "位置合わせ", True, int, r"(0|1)", "0 もしくは 1", None) - finger_alignment_result, finger_alignment_datas = self.read_csv_row(rows, row_no, 18, "指位置合わせ", False, int, r"(0|1)", "0 もしくは 1", None) - floor_alignment_result, floor_alignment_datas = self.read_csv_row(rows, row_no, 19, "床位置合わせ", False, int, r"(0|1)", "0 もしくは 1", None) - arm_alignment_length_result, arm_alignment_length_datas = self.read_csv_row(rows, row_no, 20, "手首の距離", False, float, None, None, None) - finger_alignment_length_result, finger_alignment_length_datas = self.read_csv_row(rows, row_no, 21, "指の距離", False, float, None, None, None) - floor_alignment_length_result, floor_alignment_length_datas = self.read_csv_row(rows, row_no, 22, "床との距離", False, float, None, None, None) - arm_check_skip_result, arm_check_skip_datas = self.read_csv_row(rows, row_no, 23, "腕チェックスキップ", True, int, r"(0|1)", "0 もしくは 1", None) - org_camera_motion_result, org_camera_motion_path = self.read_csv_row(rows, row_no, 24, "カメラモーションVMD", False, str, None, None, ("vmd")) - output_camera_motion_result, output_camera_motion_path = self.read_csv_row(rows, row_no, 25, "出力カメラVMD", False, str, None, None, ("vmd")) - camera_length_result, camera_length_datas = self.read_csv_row(rows, row_no, 26, "距離稼働範囲", False, float, None, None, None) - org_camera_model_result, org_camera_model_path = self.read_csv_row(rows, row_no, 27, "カメラ作成元モデルPMX", False, str, None, None, ("pmx")) - camera_y_offset_result, camera_y_offset_datas = self.read_csv_row(rows, row_no, 28, "全長Yオフセット", False, float, None, None, None) + org_motion_result, org_motion_path = self.read_csv_row(rows, row_no, 1, "調整対象モーションVMD/VPD", True, str, None, None, (".vmd", ".vpd")) + org_model_result, org_model_path = self.read_csv_row(rows, row_no, 2, "モーション作成元モデルPMX", True, str, None, None, (".pmx")) + rep_model_result, rep_model_path = self.read_csv_row(rows, row_no, 3, "モーション変換先モデルPMX", True, str, None, None, (".pmx")) + stance_center_xz_result, stance_center_xz_datas = self.read_csv_row(rows, row_no, 4, "センターXZ補正", True, int, r"^(0|1)$", "0 もしくは 1", None) + stance_upper_result, stance_upper_datas = self.read_csv_row(rows, row_no, 5, "上半身補正", True, int, r"^(0|1)$", "0 もしくは 1", None) + stance_lower_result, stance_lower_datas = self.read_csv_row(rows, row_no, 6, "下半身補正", True, int, r"^(0|1)$", "0 もしくは 1", None) + stance_leg_ik_result, stance_leg_ik_datas = self.read_csv_row(rows, row_no, 7, "足IK補正", True, int, r"^(0|1)$", "0 もしくは 1", None) + stance_toe_result, stance_toe_datas = self.read_csv_row(rows, row_no, 8, "つま先補正", True, int, r"^(0|1)$", "0 もしくは 1", None) + stance_toe_ik_result, stance_toe_ik_datas = self.read_csv_row(rows, row_no, 9, "つま先IK補正", True, int, r"^(0|1)$", "0 もしくは 1", None) + stance_shoulder_result, stance_shoulder_datas = self.read_csv_row(rows, row_no, 10, "肩補正", True, int, r"^(0|1)$", "0 もしくは 1", None) + stance_center_y_result, stance_center_y_datas = self.read_csv_row(rows, row_no, 11, "センターY補正", True, int, r"^(0|1)$", "0 もしくは 1", None) + separate_twist_result, separate_twist_datas = self.read_csv_row(rows, row_no, 12, "捩り分散", True, int, r"^(0|1)$", "0 もしくは 1", None) + morph_result, morph_datas = self.read_csv_row(rows, row_no, 13, "モーフ置換", False, str, r"[^\:]+\:[^\:]+\:\d+\.?\d*\;", "元:先:大きさ;", None) + arm_avoidance_result, arm_avoidance_datas = self.read_csv_row(rows, row_no, 14, "接触回避", True, int, r"^(0|1)$", "0 もしくは 1", None) + avoidance_name_result, avoidance_name_datas = self.read_csv_row(rows, row_no, 15, "接触回避剛体", False, str, r"[^\;]+\;", "剛体名;", None) + arm_alignment_result, arm_alignment_datas = self.read_csv_row(rows, row_no, 16, "位置合わせ", True, int, r"^(0|1)$", "0 もしくは 1", None) + finger_alignment_result, finger_alignment_datas = self.read_csv_row(rows, row_no, 17, "指位置合わせ", False, int, r"^(0|1)$", "0 もしくは 1", None) + floor_alignment_result, floor_alignment_datas = self.read_csv_row(rows, row_no, 18, "床位置合わせ", False, int, r"^(0|1)$", "0 もしくは 1", None) + arm_alignment_length_result, arm_alignment_length_datas = self.read_csv_row(rows, row_no, 19, "手首の距離", False, float, None, None, None) + finger_alignment_length_result, finger_alignment_length_datas = self.read_csv_row(rows, row_no, 20, "指の距離", False, float, None, None, None) + floor_alignment_length_result, floor_alignment_length_datas = self.read_csv_row(rows, row_no, 21, "床との距離", False, float, None, None, None) + arm_check_skip_result, arm_check_skip_datas = self.read_csv_row(rows, row_no, 22, "腕チェックスキップ", True, int, r"^(0|1)$", "0 もしくは 1", None) + org_camera_motion_result, org_camera_motion_path = self.read_csv_row(rows, row_no, 23, "カメラモーションVMD", False, str, None, None, (".vmd")) + camera_length_result, camera_length_datas = self.read_csv_row(rows, row_no, 24, "距離稼働範囲", False, float, r"^[1-9]\d*\.?\d*", "1以上", None) + org_camera_model_result, org_camera_model_path = self.read_csv_row(rows, row_no, 25, "カメラ作成元モデルPMX", False, str, None, None, (".pmx")) + camera_y_offset_result, camera_y_offset_datas = self.read_csv_row(rows, row_no, 26, "全長Yオフセット", False, float, None, None, None) - result = result & group_no_result & org_motion_result & org_model_result & rep_model_result & output_motion_result & stance_center_xz_result \ + result = result & group_no_result & org_motion_result & org_model_result & rep_model_result & stance_center_xz_result \ & stance_upper_result & stance_lower_result & stance_leg_ik_result & stance_toe_result & stance_toe_ik_result & stance_shoulder_result \ & stance_center_y_result & separate_twist_result & arm_check_skip_result & morph_result & arm_avoidance_result & avoidance_name_result \ & arm_alignment_result & finger_alignment_result & floor_alignment_result & arm_alignment_length_result & finger_alignment_length_result \ - & floor_alignment_length_result & org_camera_motion_result & output_camera_motion_result & camera_length_result & org_camera_model_result \ + & floor_alignment_length_result & org_camera_motion_result & camera_length_result & org_camera_model_result \ & camera_y_offset_result + if result: + if prev_group_no != group_no[0]: + now_model_no = 1 + + if len(service_data_txt) > 0: + # 既存データがある場合、出力 + logger.info(service_data_txt, decoration=MLogger.DECORATION_BOX) + + # 先頭モーションの場合 + service_data_txt = f"\n【グループNo.{group_no[0]}】 \n" + + arm_avoidance_txt = "あり" if arm_avoidance_datas[0] == 1 else "なし" + service_data_txt = f"{service_data_txt} 剛体接触回避: {arm_avoidance_txt}\n" + arm_alignment_txt = "あり" if arm_alignment_datas[0] == 1 else "なし" + service_data_txt = f"{service_data_txt} 手首位置合わせ: {arm_alignment_txt} ({arm_alignment_length_datas})\n" + finger_alignment_txt = "あり" if finger_alignment_datas[0] == 1 else "なし" + service_data_txt = f"{service_data_txt} 指位置合わせ: {finger_alignment_txt} ({finger_alignment_length_datas})\n" + floor_alignment_txt = "あり" if floor_alignment_datas[0] == 1 else "なし" + service_data_txt = f"{service_data_txt} 床位置合わせ: {floor_alignment_txt} ({floor_alignment_length_datas})\n" + arm_check_skip_txt = "あり" if arm_check_skip_datas[0] == 1 else "なし" + service_data_txt = f"{service_data_txt} 腕チェックスキップ: {arm_check_skip_txt}\n" + + service_data_txt = f"{service_data_txt} カメラ: {org_camera_motion_path}\n" + service_data_txt = f"{service_data_txt} 距離制限: {camera_length_datas}\n" + else: + # 複数人モーションの場合、No加算 + now_model_no += 1 + + service_data_txt = f"{service_data_txt}\n 【人物No.{now_model_no}】 --------- \n" + + service_data_txt = f"{service_data_txt}  モーション: {org_motion_path}\n" + service_data_txt = f"{service_data_txt}  作成元モデル: {org_model_path}\n" + service_data_txt = f"{service_data_txt}  変換先モデル: {rep_model_path}\n" + service_data_txt = f"{service_data_txt}  カメラ作成元モデル: {org_camera_model_path}\n" + service_data_txt = f"{service_data_txt}  Yオフセット: {camera_y_offset_datas}\n" + + detail_stance_list = [] + if stance_center_xz_datas[0] == 1: + detail_stance_list.append("センターXZ補正") + if stance_upper_datas[0] == 1: + detail_stance_list.append("上半身補正") + if stance_lower_datas[0] == 1: + detail_stance_list.append("下半身補正") + if stance_leg_ik_datas[0] == 1: + detail_stance_list.append("足IK補正") + if stance_toe_datas[0] == 1: + detail_stance_list.append("つま先補正") + if stance_toe_ik_datas[0] == 1: + detail_stance_list.append("つま先IK補正") + if stance_shoulder_datas[0] == 1: + detail_stance_list.append("肩補正") + if stance_center_y_datas[0] == 1: + detail_stance_list.append("センターY補正") + detail_stance_txt = ", ".join(detail_stance_list) + + service_data_txt = f"{service_data_txt}  スタンス追加補正有無: {detail_stance_txt}\n" + + twist_txt = "あり" if separate_twist_datas[0] == 1 else "なし" + service_data_txt = f"{service_data_txt}  捩り分散有無: {twist_txt}\n" + + # モーフデータ + morph_list = [] + for morph_data in morph_datas: + m = re.findall(r"([^\:]+)\:([^\:]+)\:(\d+\.?\d*)\;", morph_data) + morph_list.append(f"{m[0][0]} → {m[0][1]} ({float(m[0][2])})") + morph_txt = ", ".join(morph_list) + service_data_txt = f"{service_data_txt}  モーフ置換: {morph_txt}\n" + + # 接触回避データ + arm_avoidance_name_list = [] + for avoidance_data in avoidance_name_datas: + m = re.findall(r"([^\:]+)\;", avoidance_data) + arm_avoidance_name_list.append(m[0][0]) + arm_avoidance_name_txt = ", ".join(arm_avoidance_name_list) + service_data_txt = f"{service_data_txt}  対象剛体名: {arm_avoidance_name_txt}\n" + + prev_group_no = group_no[0] + if result: - # 全部OKなら処理開始 - self.load(event, 0) + if is_exec: + # 全部OKなら処理開始 + self.load(event, 0) + else: + + if len(service_data_txt) > 0: + # 既存データがある場合、最後に出力 + logger.info(service_data_txt, decoration=MLogger.DECORATION_BOX) + + # OKかつ確認のみの場合、出力して終了 + logger.info("CSVデータの確認が成功しました。", decoration=MLogger.DECORATION_BOX, title="OK") + + self.enable() + return else: - logger.error("CSVデータに不整合があるため、処理を中断します") + logger.error("CSVデータに不整合があるため、処理を中断します", decoration=MLogger.DECORATION_BOX) + + self.enable() + self.release_tab() + return def read_csv_row(self, rows: list, row_no: int, row_idx: int, row_name: str, row_required: bool, row_type: type, row_regex: str, row_regex_str: str, path_exts: tuple): try: if row_required and (len(rows) < row_idx or not rows[row_idx]): - logger.warning("%s行目の%s(%s列目)が設定されていません", row_no, row_name, row_idx + 1) + logger.warning("%s行目の%s(%s列目)が設定されていません", row_no + 1, row_name, row_idx + 1) return False, None try: @@ -214,22 +336,23 @@ def read_csv_row(self, rows: list, row_no: int, row_idx: int, row_name: str, row pass except Exception: row_type_str = "半角整数" if row_type == int else "半角数字" - logger.warning("%s行目の%s(%s列目)の型(%s)が合っていません", row_no, row_name, row_idx + 1, row_type_str) + logger.warning("%s行目の%s(%s列目)の型(%s)が合っていません", row_no + 1, row_name, row_idx + 1, row_type_str) return False, None if rows[row_idx] and row_regex and not re.findall(row_regex, rows[row_idx]): - logger.warning("%s行目の%s(%s列目)の表示形式(%s)が合っていません", row_no, row_name, row_idx + 1, row_regex_str) + logger.warning("%s行目の%s(%s列目)の表示形式(%s)が合っていません", row_no + 1, row_name, row_idx + 1, row_regex_str) return False, None if rows[row_idx] and path_exts: - if (not os.path.exists(rows[row_idx]) or not os.path.isfile(rows[row_idx])): - logger.warning("%s行目の%s(%s列目)のファイルが存在していません", row_no, row_name, row_idx + 1) + if not rows[row_idx] or (not os.path.exists(rows[row_idx]) or not os.path.isfile(rows[row_idx])): + logger.warning("%s行目の%s(%s列目)のファイルが存在していません", row_no + 1, row_name, row_idx + 1) return False, None # ファイル名・拡張子 file_name, ext = os.path.splitext(os.path.basename(rows[row_idx])) - if (not file_name and ext not in path_exts): - logger.warning("%s行目の%s(%s列目)のファイル拡張子(%s)が合っていません", row_no, row_name, row_idx + 1, ','.join(map(str, path_exts))) + if (ext not in path_exts): + logger.warning("%s行目の%s(%s列目)のファイル拡張子(%s)が合っていません", row_no + 1, row_name, row_idx + 1, \ + ','.join(map(str, path_exts)) if len(path_exts) > 1 else path_exts) return False, None # 読み取り実施 @@ -249,7 +372,7 @@ def read_csv_row(self, rows: list, row_no: int, row_idx: int, row_name: str, row return True, rows[row_idx] except Exception as e: - logger.warning("%s行目の%s(%s列目)の読み取りに失敗しました\n%s", row_no, row_name, row_idx + 1, e) + logger.warning("%s行目の%s(%s列目)の読み取りに失敗しました\n%s", row_no + 1, row_name, row_idx + 1, e) return False, None # 読み込み @@ -287,34 +410,32 @@ def load(self, event, line_idx): break group_no_result, group_no = self.read_csv_row(rows, row_no, 0, "グループNo", True, int, r"\d+", "数値のみ", None) - org_motion_result, org_motion_path = self.read_csv_row(rows, row_no, 1, "調整対象モーションVMD/VPD", True, str, None, None, ("vmd", "vpd")) - org_model_result, org_model_path = self.read_csv_row(rows, row_no, 2, "モーション作成元モデルPMX", True, str, None, None, ("pmx")) - rep_model_result, rep_model_path = self.read_csv_row(rows, row_no, 3, "モーション変換先モデルPMX", True, str, None, None, ("pmx")) - output_motion_result, output_motion_path = self.read_csv_row(rows, row_no, 4, "出力VMD", False, str, None, None, ("vmd")) - stance_center_xz_result, stance_center_xz_datas = self.read_csv_row(rows, row_no, 5, "センターXZ補正", True, int, r"(0|1)", "0 もしくは 1", None) - stance_upper_result, stance_upper_datas = self.read_csv_row(rows, row_no, 6, "上半身補正", True, int, r"(0|1)", "0 もしくは 1", None) - stance_lower_result, stance_lower_datas = self.read_csv_row(rows, row_no, 7, "下半身補正", True, int, r"(0|1)", "0 もしくは 1", None) - stance_leg_ik_result, stance_leg_ik_datas = self.read_csv_row(rows, row_no, 8, "足IK補正", True, int, r"(0|1)", "0 もしくは 1", None) - stance_toe_result, stance_toe_datas = self.read_csv_row(rows, row_no, 9, "つま先補正", True, int, r"(0|1)", "0 もしくは 1", None) - stance_toe_ik_result, stance_toe_ik_datas = self.read_csv_row(rows, row_no, 10, "つま先IK補正", True, int, r"(0|1)", "0 もしくは 1", None) - stance_shoulder_result, stance_shoulder_datas = self.read_csv_row(rows, row_no, 11, "肩補正", True, int, r"(0|1)", "0 もしくは 1", None) - stance_center_y_result, stance_center_y_datas = self.read_csv_row(rows, row_no, 12, "センターY補正", True, int, r"(0|1)", "0 もしくは 1", None) - separate_twist_result, separate_twist_datas = self.read_csv_row(rows, row_no, 13, "捩り分散", True, int, r"(0|1)", "0 もしくは 1", None) - morph_result, morph_datas = self.read_csv_row(rows, row_no, 14, "モーフ置換", False, str, r"[^\:]+\:[^\:]+\:\d+\.?\d*\;", "元:先:大きさ;", None) - arm_avoidance_result, arm_avoidance_datas = self.read_csv_row(rows, row_no, 15, "接触回避", True, int, r"(0|1)", "0 もしくは 1", None) - avoidance_name_result, avoidance_name_datas = self.read_csv_row(rows, row_no, 16, "接触回避剛体", False, str, r"[^\;]+\;", "剛体名;", None) - arm_alignment_result, arm_alignment_datas = self.read_csv_row(rows, row_no, 17, "位置合わせ", True, int, r"(0|1)", "0 もしくは 1", None) - finger_alignment_result, finger_alignment_datas = self.read_csv_row(rows, row_no, 18, "指位置合わせ", False, int, r"(0|1)", "0 もしくは 1", None) - floor_alignment_result, floor_alignment_datas = self.read_csv_row(rows, row_no, 19, "床位置合わせ", False, int, r"(0|1)", "0 もしくは 1", None) - arm_alignment_length_result, arm_alignment_length_datas = self.read_csv_row(rows, row_no, 20, "手首の距離", False, float, None, None, None) - finger_alignment_length_result, finger_alignment_length_datas = self.read_csv_row(rows, row_no, 21, "指の距離", False, float, None, None, None) - floor_alignment_length_result, floor_alignment_length_datas = self.read_csv_row(rows, row_no, 22, "床との距離", False, float, None, None, None) - arm_check_skip_result, arm_check_skip_datas = self.read_csv_row(rows, row_no, 23, "腕チェックスキップ", True, int, r"(0|1)", "0 もしくは 1", None) - org_camera_motion_result, org_camera_motion_path = self.read_csv_row(rows, row_no, 24, "カメラモーションVMD", False, str, None, None, ("vmd")) - output_camera_motion_result, output_camera_motion_path = self.read_csv_row(rows, row_no, 25, "出力カメラVMD", False, str, None, None, ("vmd")) - camera_length_result, camera_length_datas = self.read_csv_row(rows, row_no, 26, "距離稼働範囲", False, float, None, None, None) - org_camera_model_result, org_camera_model_path = self.read_csv_row(rows, row_no, 27, "カメラ作成元モデルPMX", False, str, None, None, ("pmx")) - camera_y_offset_result, camera_y_offset_datas = self.read_csv_row(rows, row_no, 28, "全長Yオフセット", False, float, None, None, None) + org_motion_result, org_motion_path = self.read_csv_row(rows, row_no, 1, "調整対象モーションVMD/VPD", True, str, None, None, (".vmd", ".vpd")) + org_model_result, org_model_path = self.read_csv_row(rows, row_no, 2, "モーション作成元モデルPMX", True, str, None, None, (".pmx")) + rep_model_result, rep_model_path = self.read_csv_row(rows, row_no, 3, "モーション変換先モデルPMX", True, str, None, None, (".pmx")) + stance_center_xz_result, stance_center_xz_datas = self.read_csv_row(rows, row_no, 4, "センターXZ補正", True, int, r"^(0|1)$", "0 もしくは 1", None) + stance_upper_result, stance_upper_datas = self.read_csv_row(rows, row_no, 5, "上半身補正", True, int, r"^(0|1)$", "0 もしくは 1", None) + stance_lower_result, stance_lower_datas = self.read_csv_row(rows, row_no, 6, "下半身補正", True, int, r"^(0|1)$", "0 もしくは 1", None) + stance_leg_ik_result, stance_leg_ik_datas = self.read_csv_row(rows, row_no, 7, "足IK補正", True, int, r"^(0|1)$", "0 もしくは 1", None) + stance_toe_result, stance_toe_datas = self.read_csv_row(rows, row_no, 8, "つま先補正", True, int, r"^(0|1)$", "0 もしくは 1", None) + stance_toe_ik_result, stance_toe_ik_datas = self.read_csv_row(rows, row_no, 9, "つま先IK補正", True, int, r"^(0|1)$", "0 もしくは 1", None) + stance_shoulder_result, stance_shoulder_datas = self.read_csv_row(rows, row_no, 10, "肩補正", True, int, r"^(0|1)$", "0 もしくは 1", None) + stance_center_y_result, stance_center_y_datas = self.read_csv_row(rows, row_no, 11, "センターY補正", True, int, r"^(0|1)$", "0 もしくは 1", None) + separate_twist_result, separate_twist_datas = self.read_csv_row(rows, row_no, 12, "捩り分散", True, int, r"^(0|1)$", "0 もしくは 1", None) + morph_result, morph_datas = self.read_csv_row(rows, row_no, 13, "モーフ置換", False, str, r"[^\:]+\:[^\:]+\:\d+\.?\d*\;", "元:先:大きさ;", None) + arm_avoidance_result, arm_avoidance_datas = self.read_csv_row(rows, row_no, 14, "接触回避", True, int, r"^(0|1)$", "0 もしくは 1", None) + avoidance_name_result, avoidance_name_datas = self.read_csv_row(rows, row_no, 15, "接触回避剛体", False, str, r"[^\;]+\;", "剛体名;", None) + arm_alignment_result, arm_alignment_datas = self.read_csv_row(rows, row_no, 16, "位置合わせ", True, int, r"^(0|1)$", "0 もしくは 1", None) + finger_alignment_result, finger_alignment_datas = self.read_csv_row(rows, row_no, 17, "指位置合わせ", False, int, r"^(0|1)$", "0 もしくは 1", None) + floor_alignment_result, floor_alignment_datas = self.read_csv_row(rows, row_no, 18, "床位置合わせ", False, int, r"^(0|1)$", "0 もしくは 1", None) + arm_alignment_length_result, arm_alignment_length_datas = self.read_csv_row(rows, row_no, 19, "手首の距離", False, float, None, None, None) + finger_alignment_length_result, finger_alignment_length_datas = self.read_csv_row(rows, row_no, 20, "指の距離", False, float, None, None, None) + floor_alignment_length_result, floor_alignment_length_datas = self.read_csv_row(rows, row_no, 21, "床との距離", False, float, None, None, None) + arm_check_skip_result, arm_check_skip_datas = self.read_csv_row(rows, row_no, 22, "腕チェックスキップ", True, int, r"^(0|1)$", "0 もしくは 1", None) + org_camera_motion_result, org_camera_motion_path = self.read_csv_row(rows, row_no, 23, "カメラモーションVMD", False, str, None, None, (".vmd")) + camera_length_result, camera_length_datas = self.read_csv_row(rows, row_no, 24, "距離稼働範囲", False, float, None, None, None) + org_camera_model_result, org_camera_model_path = self.read_csv_row(rows, row_no, 25, "カメラ作成元モデルPMX", False, str, None, None, (".pmx")) + camera_y_offset_result, camera_y_offset_datas = self.read_csv_row(rows, row_no, 26, "全長Yオフセット", False, float, None, None, None) if now_motion_idx == 0: # 複数パネルはクリア @@ -324,7 +445,7 @@ def load(self, event, line_idx): self.frame.file_panel_ctrl.file_set.motion_vmd_file_ctrl.file_ctrl.SetPath(org_motion_path) self.frame.file_panel_ctrl.file_set.org_model_file_ctrl.file_ctrl.SetPath(org_model_path) self.frame.file_panel_ctrl.file_set.rep_model_file_ctrl.file_ctrl.SetPath(rep_model_path) - self.frame.file_panel_ctrl.file_set.output_vmd_file_ctrl.file_ctrl.SetPath(output_motion_path) + self.frame.file_panel_ctrl.file_set.output_vmd_file_ctrl.file_ctrl.SetPath("") self.frame.file_panel_ctrl.file_set.org_model_file_ctrl.title_parts_ctrl.SetValue( stance_center_xz_datas[0] | stance_upper_datas[0] | stance_lower_datas[0] | stance_leg_ik_datas[0] | \ @@ -383,7 +504,7 @@ def load(self, event, line_idx): # カメラ self.frame.camera_panel_ctrl.camera_vmd_file_ctrl.file_ctrl.SetPath(org_camera_motion_path) - self.frame.camera_panel_ctrl.output_camera_vmd_file_ctrl.file_ctrl.SetPath(output_camera_motion_path) + self.frame.camera_panel_ctrl.output_camera_vmd_file_ctrl.file_ctrl.SetPath("") self.frame.camera_panel_ctrl.camera_length_slider.SetValue(camera_length_datas) # カメラ元情報 @@ -402,7 +523,7 @@ def load(self, event, line_idx): self.frame.multi_panel_ctrl.file_set_list[now_motion_idx - 1].motion_vmd_file_ctrl.file_ctrl.SetPath(org_motion_path) self.frame.multi_panel_ctrl.file_set_list[now_motion_idx - 1].org_model_file_ctrl.file_ctrl.SetPath(org_model_path) self.frame.multi_panel_ctrl.file_set_list[now_motion_idx - 1].rep_model_file_ctrl.file_ctrl.SetPath(rep_model_path) - self.frame.multi_panel_ctrl.file_set_list[now_motion_idx - 1].output_vmd_file_ctrl.file_ctrl.SetPath(output_motion_path) + self.frame.multi_panel_ctrl.file_set_list[now_motion_idx - 1].output_vmd_file_ctrl.file_ctrl.SetPath("") self.frame.multi_panel_ctrl.file_set_list[now_motion_idx - 1].org_model_file_ctrl.title_parts_ctrl.SetValue( stance_center_xz_datas[0] | stance_upper_datas[0] | stance_lower_datas[0] | stance_leg_ik_datas[0] | \ @@ -443,6 +564,9 @@ def load(self, event, line_idx): m = re.findall(r"([^\:]+)\;", avoidance_data) self.frame.arm_panel_ctrl.bulk_avoidance_set_dict[now_motion_idx - 1].append(m[0][0]) + # 指位置合わせは常に0(ダイアログ防止) + self.frame.arm_panel_ctrl.arm_alignment_finger_flg_ctrl.SetValue(0) + # カメラ元情報 self.frame.camera_panel_ctrl.initialize(event) self.frame.camera_panel_ctrl.camera_set_dict[now_motion_idx + 1].camera_model_file_ctrl.file_ctrl.SetPath(org_camera_model_path) diff --git a/src/mmd/VmdData.pyx b/src/mmd/VmdData.pyx index 80ce9e0..f8fa138 100644 --- a/src/mmd/VmdData.pyx +++ b/src/mmd/VmdData.pyx @@ -569,12 +569,14 @@ cdef class VmdMotion: prev_sep_fno = 0 # キーフレを取得する + suffix = "" if start_fno < 0 and end_fno < 0: # 範囲指定がない場合、全範囲 fnos = self.get_bone_fnos(bone_name) else: # 範囲指定がある場合はその範囲内だけ fnos = self.get_bone_fnos(bone_name, start_fno=start_fno, end_fno=end_fno) + suffix = f"({start_fno}-{end_fno})" logger.debug("remove_unnecessary_bf prev: %s, %s", bone_name, len(fnos)) if len(fnos) > 2: @@ -625,9 +627,9 @@ cdef class VmdMotion: if fno // 100 > prev_sep_fno: if data_set_no == 0: - logger.info("-- %sフレーム目:終了(%s%)【不要キー削除 - %s】", fno, round((fno / fnos[-1]) * 100, 3), bone_name) + logger.info("-- %sフレーム目:終了(%s%)【不要キー削除%s - %s】", fno, round((fno / fnos[-1]) * 100, 3), suffix, bone_name) else: - logger.info("-- %sフレーム目:終了(%s%)【No.%s - 不要キー削除 - %s】", fno, round((fno / fnos[-1]) * 100, 3), data_set_no, bone_name) + logger.info("-- %sフレーム目:終了(%s%)【No.%s - 不要キー削除%s - %s】", fno, round((fno / fnos[-1]) * 100, 3), data_set_no, suffix, bone_name) prev_sep_fno = fno // 100 diff --git a/src/service/SizingService.py b/src/service/SizingService.py index 2dbe70f..76f4e69 100644 --- a/src/service/SizingService.py +++ b/src/service/SizingService.py @@ -54,6 +54,13 @@ def execute(self): service_data_txt = "{service_data_txt}  捩り分散有無: {twist_flg}\n".format(service_data_txt=service_data_txt, twist_flg=data_set.twist_flg) # noqa + morph_list = [] + for (org_morph_name, rep_morph_name, morph_ratio) in data_set.morph_list: + morph_list.append(f"{org_morph_name} → {rep_morph_name} ({morph_ratio})") + morph_txt = ", ".join(morph_list) + service_data_txt = "{service_data_txt}  モーフ置換: {morph_txt}\n".format(service_data_txt=service_data_txt, + morph_txt=morph_txt) # noqa + if data_set_idx in self.options.arm_options.avoidance_target_list: service_data_txt = "{service_data_txt}  対象剛体名: {avoidance_target}\n".format(service_data_txt=service_data_txt, avoidance_target=", ".join(self.options.arm_options.avoidance_target_list[data_set_idx])) # noqa diff --git a/vmdising_np64.spec b/vmdising_np64.spec index 53f765f..7069f99 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,7 +26,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β61_64bit', + name='VmdSizing_5.01_β62_64bit', debug=False, bootloader_ignore_signals=False, strip=False, From 473abf5548867bb3252ea16b2f2644f7ded7e4d2 Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Sat, 31 Oct 2020 13:48:02 +0900 Subject: [PATCH 29/37] =?UTF-8?q?5.01=5F=CE=B263?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VmdSizing_5.01_β63 miumiu ・モーフ置換、接触回避  ・一括CSV用コピーボタン追加(デフォルトでクリップボードにまでコピー) ・一括サイジング  ・CSVファイルの存在チェック他追加   ・エラーだった場合、制御を戻して終了する  ・モーフ置換が指定されている場合、ファイル名にMを付与する --- archive/Readme.txt | 4 ++-- src/executor.py | 2 +- src/form/panel/ArmPanel.py | 28 ++++++++++++++++++++++++++-- src/form/panel/BulkPanel.py | 9 ++++++++- src/form/panel/MorphPanel.py | 25 +++++++++++++++++++++++++ src/form/parts/SizingFileSet.py | 3 ++- vmdising_np64.spec | 2 +- 7 files changed, 65 insertions(+), 8 deletions(-) diff --git a/archive/Readme.txt b/archive/Readme.txt index fd7673a..ba19b73 100644 --- a/archive/Readme.txt +++ b/archive/Readme.txt @@ -242,12 +242,12 @@ A ut@Cvuv^úu蕪Uv`FbN{bNXB0:A1:L̂ꂩK{łB 15 c [tu u[tv^úu[tuṽZbgBuu[t:u惂[t:傫;v1‚̃ZbgƂĂB - iŌ̃Z~R͕K{łj + i[tɃZ~RĂƎg܂BŌ̃Z~R͕K{łj 16 c ڐG urv^úuڐGv`FbN{bNXB0:A1:L̂ꂩK{łB 17 c ڐG urv^úuڐGv̑Ώۍ̖XgBu̖;v̂悤ɔpZ~RŌqłB - iŌ̃Z~R͕K{łj + i̖ɃZ~RĂƎg܂BŌ̃Z~R͕K{łj 18 c ʒu킹 urv^úuʒu킹v`FbN{bNXB0:A1:L̂ꂩK{łB 19 c wʒu킹 diff --git a/src/executor.py b/src/executor.py index c95838a..da2eebc 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00_β62" +VERSION_NAME = "ver5.00_β63" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/form/panel/ArmPanel.py b/src/form/panel/ArmPanel.py index 6e1cdcf..c7390f8 100644 --- a/src/form/panel/ArmPanel.py +++ b/src/form/panel/ArmPanel.py @@ -399,7 +399,7 @@ def __init__(self, frame: wx.Frame, panel: wx.Panel, window: wx.Window, set_idx: self.file_set = file_set self.rep_model_digest = 0 if not file_set.rep_model_file_ctrl.data else file_set.rep_model_file_ctrl.data.digest self.rep_avoidances = ["頭接触回避 (頭)"] # 選択肢文言 - self.rep_avoidance_names = ["頭接触回避"] # 選択肢文言に紐付くモーフ名 + self.rep_avoidance_names = ["頭接触回避"] # 選択肢文言に紐付く剛体名 self.rep_choices = None self.set_sizer = wx.StaticBoxSizer(wx.StaticBox(self.window, wx.ID_ANY, "【No.{0}】".format(set_idx)), orient=wx.VERTICAL) @@ -420,11 +420,35 @@ def __init__(self, frame: wx.Frame, panel: wx.Panel, window: wx.Window, set_idx: # 頭接触回避はデフォルトで選択 self.rep_choices.SetSelection(0) self.set_sizer.Add(self.rep_choices, 0, wx.ALL, 5) + + # 一括用コピーボタン + self.copy_btn_ctrl = wx.Button(self.window, wx.ID_ANY, u"一括用コピー", wx.DefaultPosition, wx.DefaultSize, 0) + self.copy_btn_ctrl.SetToolTip(u"接触回避データを一括CSVの形式に合わせてクリップボードにコピーします") + self.copy_btn_ctrl.Bind(wx.EVT_BUTTON, self.on_copy) + self.set_sizer.Add(self.copy_btn_ctrl, 0, wx.ALL, 5) else: self.no_data_txt = wx.StaticText(self.window, wx.ID_ANY, u"データなし", wx.DefaultPosition, wx.DefaultSize, 0) self.no_data_txt.Wrap(-1) self.set_sizer.Add(self.no_data_txt, 0, wx.ALL, 5) + def on_copy(self, event: wx.Event): + # 一括CSV用モーフテキスト生成 + avoidance_txt_list = [] + for idx in self.rep_choices.GetSelections(): + avoidance_txt_list.append(f"{self.rep_avoidance_names[idx]}") + # 文末セミコロン + avoidance_txt_list.append("") + + if wx.TheClipboard.Open(): + wx.TheClipboard.SetData(wx.TextDataObject(";".join(avoidance_txt_list))) + wx.TheClipboard.Close() + + with wx.TextEntryDialog(self.frame, u"一括CSV用の接触回避データを出力します。\n" \ + + "ダイアログを表示した時点で、下記接触回避データがクリップボードにコピーされています。\n" \ + + "コピーできてなかった場合、ボックス内の文字列を選択して、CSVに貼り付けてください。", caption=u"一括CSV用接触回避データ", + value=";".join(avoidance_txt_list), style=wx.TextEntryDialogStyle, pos=wx.DefaultPosition) as dialog: + dialog.ShowModal() + # 現在のファイルセットのハッシュと同じであるかチェック def equal_hashdigest(self, now_file_set: SizingFileSet): return self.rep_model_digest == now_file_set.rep_model_file_ctrl.data.digest @@ -433,7 +457,7 @@ def equal_hashdigest(self, now_file_set: SizingFileSet): class AvoidanceDialog(wx.Dialog): def __init__(self, parent): - super().__init__(parent, id=wx.ID_ANY, title="接触回避剛体選択", pos=(-1, -1), size=(700, 450), style=wx.DEFAULT_DIALOG_STYLE, name="AvoidanceDialog") + super().__init__(parent, id=wx.ID_ANY, title="接触回避剛体選択", pos=(-1, -1), size=(800, 500), style=wx.DEFAULT_DIALOG_STYLE, name="AvoidanceDialog") self.sizer = wx.BoxSizer(wx.VERTICAL) diff --git a/src/form/panel/BulkPanel.py b/src/form/panel/BulkPanel.py index f1e130f..976d196 100644 --- a/src/form/panel/BulkPanel.py +++ b/src/form/panel/BulkPanel.py @@ -179,6 +179,12 @@ def check(self, event: wx.Event, is_exec: bool): # タブ固定 self.fix_tab() + if not self.bulk_csv_file_ctrl.is_valid(): + # CSVパスが無効な場合、終了 + self.enable() + self.release_tab() + return + result = True with open(self.bulk_csv_file_ctrl.path(), encoding='cp932', mode='r') as f: reader = csv.reader(f) @@ -296,7 +302,7 @@ def check(self, event: wx.Event, is_exec: bool): arm_avoidance_name_list = [] for avoidance_data in avoidance_name_datas: m = re.findall(r"([^\:]+)\;", avoidance_data) - arm_avoidance_name_list.append(m[0][0]) + arm_avoidance_name_list.append(m[0]) arm_avoidance_name_txt = ", ".join(arm_avoidance_name_list) service_data_txt = f"{service_data_txt}  対象剛体名: {arm_avoidance_name_txt}\n" @@ -316,6 +322,7 @@ def check(self, event: wx.Event, is_exec: bool): logger.info("CSVデータの確認が成功しました。", decoration=MLogger.DECORATION_BOX, title="OK") self.enable() + self.release_tab() return else: logger.error("CSVデータに不整合があるため、処理を中断します", decoration=MLogger.DECORATION_BOX) diff --git a/src/form/panel/MorphPanel.py b/src/form/panel/MorphPanel.py index 0ced4f2..7faeb3f 100644 --- a/src/form/panel/MorphPanel.py +++ b/src/form/panel/MorphPanel.py @@ -200,6 +200,12 @@ def __init__(self, frame: wx.Frame, panel: wx.Panel, window: wx.Window, set_idx: self.btn_sizer = wx.BoxSizer(wx.HORIZONTAL) + # 一括用コピーボタン + self.copy_btn_ctrl = wx.Button(self.window, wx.ID_ANY, u"一括用コピー", wx.DefaultPosition, wx.DefaultSize, 0) + self.copy_btn_ctrl.SetToolTip(u"モーフ置換データを一括CSVの形式に合わせてクリップボードにコピーします") + self.copy_btn_ctrl.Bind(wx.EVT_BUTTON, self.on_copy) + self.btn_sizer.Add(self.copy_btn_ctrl, 0, wx.ALL, 5) + # インポートボタン self.import_btn_ctrl = wx.Button(self.window, wx.ID_ANY, u"インポート ...", wx.DefaultPosition, wx.DefaultSize, 0) self.import_btn_ctrl.SetToolTip(u"モーフ置換データをCSVファイルから読み込みます。\nファイル選択ダイアログが開きます。") @@ -338,6 +344,25 @@ def equal_hashdigest(self, now_file_set: SizingFileSet): return self.vmd_digest == now_file_set.motion_vmd_file_ctrl.data.digest \ and self.org_model_digest == now_file_set.org_model_file_ctrl.data.digest \ and self.rep_model_digest == now_file_set.rep_model_file_ctrl.data.digest + + def on_copy(self, event: wx.Event): + # 一括CSV用モーフテキスト生成 + morph_txt_list = [] + morph_list = self.get_morph_list() + for (om, rm, r) in morph_list: + morph_txt_list.append(f"{om}:{rm}:{r}") + # 文末セミコロン + morph_txt_list.append("") + + if wx.TheClipboard.Open(): + wx.TheClipboard.SetData(wx.TextDataObject(";".join(morph_txt_list))) + wx.TheClipboard.Close() + + with wx.TextEntryDialog(self.frame, u"一括CSV用のモーフデータを出力します。\n" \ + + "ダイアログを表示した時点で、下記モーフデータがクリップボードにコピーされています。\n" \ + + "コピーできてなかった場合、ボックス内の文字列を選択して、CSVに貼り付けてください。", caption=u"一括CSV用モーフデータ", + value=";".join(morph_txt_list), style=wx.TextEntryDialogStyle, pos=wx.DefaultPosition) as dialog: + dialog.ShowModal() def on_import(self, event: wx.Event): input_morph_path = MFileUtils.get_output_morph_path( diff --git a/src/form/parts/SizingFileSet.py b/src/form/parts/SizingFileSet.py index ff9b62a..060191a 100644 --- a/src/form/parts/SizingFileSet.py +++ b/src/form/parts/SizingFileSet.py @@ -238,7 +238,8 @@ def set_output_vmd_path(self, event, is_force=False): self.rep_model_file_ctrl.title_parts_ctrl.GetValue(), self.frame.arm_panel_ctrl.arm_process_flg_avoidance.GetValue(), self.frame.arm_panel_ctrl.arm_process_flg_alignment.GetValue(), - (self.set_no in self.frame.morph_panel_ctrl.morph_set_dict and self.frame.morph_panel_ctrl.morph_set_dict[self.set_no].is_set_morph()), + (self.set_no in self.frame.morph_panel_ctrl.morph_set_dict and self.frame.morph_panel_ctrl.morph_set_dict[self.set_no].is_set_morph()) \ + or (self.set_no in self.frame.morph_panel_ctrl.bulk_morph_set_dict and len(self.frame.morph_panel_ctrl.bulk_morph_set_dict[self.set_no]) > 0), self.output_vmd_file_ctrl.file_ctrl.GetPath(), is_force) self.output_vmd_file_ctrl.file_ctrl.SetPath(output_vmd_path) diff --git a/vmdising_np64.spec b/vmdising_np64.spec index 7069f99..02ca1be 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,7 +26,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β62_64bit', + name='VmdSizing_5.01_β63_64bit', debug=False, bootloader_ignore_signals=False, strip=False, From 3e338621d78276b2317a1f4ed0c6a79a077d1a80 Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Sun, 1 Nov 2020 10:28:20 +0900 Subject: [PATCH 30/37] Update Readme.txt --- archive/Readme.txt | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/archive/Readme.txt b/archive/Readme.txt index ba19b73..ac65ae2 100644 --- a/archive/Readme.txt +++ b/archive/Readme.txt @@ -59,7 +59,8 @@ https://seiga.nicovideo.jp/seiga/im9755721 ---------------------------------------------------------------- E{Iɂexê܂܋NĂΑvłB -@nCXybNł́Aŕ񏈗ŝŁAʏł1.3{炢s܂B + +EnCXybNł́Aŕ񏈗ŝŁAʏł1.3{炢s܂B @Aׂ̕傫Ȃ̂ŁAPC̐\vĕ̕悳łB EOo͔ł́Av4̍̂悤ɁAoVMDt@CpXƓƂɃOt@Co͂܂B @@ -393,6 +394,18 @@ https://github.com/miu200521358/vmd_sizing @ ---------------------------------------------------------------- +ver5.01@i2020/11/07j +@@E̍i30`50%Jbgj +@@EiXe[^X\@\lj +@@EꊇTCWO@\lj +@@E蕪U̐x +@@EJTCWOɁA“͈͐IvVlj +@@EX[WOɁAΏۃ{[w@\lj +@@EBugfix: JTCWOŎXikڔ䗦jȂĂ̂C +@@EBugfix: vpdʒu킹ƎsĂ̂C +@@EBugfix: ڐGœ{[ɃEFCgĂ钸_‚Ȃ +@@@@@@@@@ڐGw肵ĂꍇɁAG[Ă̂C + ver5.00@i2020/07/05j @@EwCu PyQt5 numpy ɕύXɔA蒼 @@El[VɑΉ @@ -413,7 +426,7 @@ ver4.03 @@EtBK[^bg[VpɁuwʒu킹vIvVlj @@Eʒu킹IvV̏𒲐 @@Eʒu킹IvṼZ^[㉺p[^[ -@@EBugix: ʒu킹IvVŁuӐ}ʃG[vĂ̂C +@@EBugfix: ʒu킹IvVŁuӐ}ʃG[vĂ̂C ver4.02@i2019/11/17j @@Eʒu킹IvV̎⑫Ə̋p[^[O From 3a9452884482a9d63cd4679eedb54d806892746d Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Sun, 1 Nov 2020 12:21:38 +0900 Subject: [PATCH 31/37] =?UTF-8?q?5.01=5F=CE=B264?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VmdSizing_5.01_β64 miumiu ・履歴ファイルをUTF-8にて保存・読み込みに変更  ・exeに固めた時点で日本語圏だからcp932なのかもしれない   cp932で保存されている履歴は一旦UTF-8変換してから再処理(既存履歴はそのまま使える)  ・ハングル語圏のユーザーさんよりご報告あり --- src/executor.py | 2 +- src/utils/MFileutils.py | 33 ++++++++++++++++++++++++++------- vmdising_np64.spec | 2 +- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/executor.py b/src/executor.py index da2eebc..48b027b 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00_β63" +VERSION_NAME = "ver5.00_β64" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/utils/MFileutils.py b/src/utils/MFileutils.py index 5ed4de2..2bca2f1 100644 --- a/src/utils/MFileutils.py +++ b/src/utils/MFileutils.py @@ -9,6 +9,7 @@ import traceback from pathlib import Path import re +import _pickle as cPickle from utils.MLogger import MLogger # noqa @@ -25,20 +26,38 @@ def resource_path(relative): # ファイル履歴読み込み def read_history(mydir_path): # ファイル履歴 - file_hitories = {"vmd": [], "org_pmx": [], "rep_pmx": [], "camera_vmd": [], "camera_pmx": [], "smooth_vmd": [], "smooth_pmx": [], "bulk_csv": [], "max": 50} + base_file_hitories = {"vmd": [], "org_pmx": [], "rep_pmx": [], "camera_vmd": [], "camera_pmx": [], "smooth_vmd": [], "smooth_pmx": [], "bulk_csv": [], "max": 50} + file_hitories = cPickle.loads(cPickle.dumps(base_file_hitories, -1)) # 履歴JSONファイルがあれば読み込み try: - with open(os.path.join(mydir_path, 'history.json'), 'r') as f: + with open(os.path.join(mydir_path, 'history.json'), 'r', encoding="utf-8") as f: file_hitories = json.load(f) # キーが揃っているかチェック - for key in ["vmd", "org_pmx", "rep_pmx", "camera_vmd", "camera_pmx", "smooth_pmx", "smooth_vmd", "bulk_csv"]: + for key in base_file_hitories.keys(): if key not in file_hitories: file_hitories[key] = [] # 最大件数は常に上書き file_hitories["max"] = 50 except Exception: - file_hitories = {"vmd": [], "org_pmx": [], "rep_pmx": [], "camera_vmd": [], "camera_pmx": [], "smooth_vmd": [], "smooth_pmx": [], "bulk_csv": [], "max": 50} + # UTF-8で読み込めなかった場合、デフォルトで読み込んでUTF-8変換 + try: + with open(os.path.join(mydir_path, 'history.json'), 'r') as f: + file_hitories = json.load(f) + # キーが揃っているかチェック + for key in base_file_hitories.keys(): + if key not in file_hitories: + file_hitories[key] = [] + # 最大件数は常に上書き + file_hitories["max"] = 50 + + # 一旦UTF-8で出力 + save_history(mydir_path, file_hitories) + + # UTF-8で読み込みし直し + return read_history(mydir_path) + except Exception: + file_hitories = cPickle.loads(cPickle.dumps(base_file_hitories, -1)) return file_hitories @@ -46,10 +65,10 @@ def read_history(mydir_path): def save_history(mydir_path, file_hitories): # 入力履歴を保存 try: - with open(os.path.join(mydir_path, 'history.json'), 'w') as f: + with open(os.path.join(mydir_path, 'history.json'), 'w', encoding="utf-8") as f: json.dump(file_hitories, f, ensure_ascii=False) - except Exception: - logger.error("履歴ファイル保存失敗", traceback.format_exc()) + except Exception as e: + logger.error("履歴ファイルの保存に失敗しました", e, decoration=MLogger.DECORATION_BOX) # パス解決 diff --git a/vmdising_np64.spec b/vmdising_np64.spec index 02ca1be..e46a455 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,7 +26,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β63_64bit', + name='VmdSizing_5.01_β64_64bit', debug=False, bootloader_ignore_signals=False, strip=False, From caa55e4d9321097ce51cf5d19c7473d2583ae150 Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Tue, 3 Nov 2020 06:10:29 +0900 Subject: [PATCH 32/37] =?UTF-8?q?5.01=5F=CE=B265?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VmdSizing_5.01_β65 miumiu ・総回転量計算時に、付与親が指定されており、かつ付与率がゼロの場合に、計算に失敗していたのを修正  → 付与率がゼロの場合、ゼロクォータニオンで計算 ・カメラ  ・Yオフセットの表示修正 ・リドミをリリース用に合わせて修正 --- archive/Readme.txt | 3 +-- src/executor.py | 2 +- src/mmd/PmxData.pyx | 2 +- src/service/parts/CameraService.py | 16 +++++++++------- src/utils/MServiceUtils.pyx | 6 +++++- vmdising_np64.spec | 2 +- 6 files changed, 18 insertions(+), 13 deletions(-) diff --git a/archive/Readme.txt b/archive/Readme.txt index ac65ae2..e8fb688 100644 --- a/archive/Readme.txt +++ b/archive/Readme.txt @@ -395,12 +395,11 @@ https://github.com/miu200521358/vmd_sizing ---------------------------------------------------------------- ver5.01@i2020/11/07j -@@E̍i30`50%Jbgj +@@E̍i30%Xs[hAbvj @@EiXe[^X\@\lj @@EꊇTCWO@\lj @@E蕪U̐x @@EJTCWOɁA“͈͐IvVlj -@@EX[WOɁAΏۃ{[w@\lj @@EBugfix: JTCWOŎXikڔ䗦jȂĂ̂C @@EBugfix: vpdʒu킹ƎsĂ̂C @@EBugfix: ڐGœ{[ɃEFCgĂ钸_‚Ȃ diff --git a/src/executor.py b/src/executor.py index 48b027b..6043058 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00_β64" +VERSION_NAME = "ver5.00_β65" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/mmd/PmxData.pyx b/src/mmd/PmxData.pyx index 52d8f7d..50dd9dd 100644 --- a/src/mmd/PmxData.pyx +++ b/src/mmd/PmxData.pyx @@ -1096,7 +1096,7 @@ cdef class PmxModel: start_type_bone = target_bone_name[1:] # 自分をリンクに登録 - links.append(self.bones[target_bone_name]) + links.append(self.bones[target_bone_name].copy()) parent_name = None if is_defined: diff --git a/src/service/parts/CameraService.py b/src/service/parts/CameraService.py index 45af5c8..fec4f8c 100644 --- a/src/service/parts/CameraService.py +++ b/src/service/parts/CameraService.py @@ -681,8 +681,8 @@ def prepare_ratio(self, data_set_idx: int, org_model: PmxModel, rep_model: PmxMo # 顔の大きさ比率 head_ratio = rep_face_length / org_face_length - logger.info("【No.%s】作成元モデル 全長: %s, 頭身: %s, 顔の大きさ: %s, Yオフセット: %s", (data_set_idx + 1), round(org_total_height, 5), round(org_heads, 5), round(org_face_length, 5), data_set.camera_offset_y) - logger.info("【No.%s】変換先モデル 全長: %s, 頭身: %s, 顔の大きさ: %s", (data_set_idx + 1), round(rep_total_height, 5), round(rep_heads, 5), round(rep_face_length, 5)) + logger.info("【No.%s】作成元モデル 全長: %s, 頭身: %s, 顔の大きさ: %s", (data_set_idx + 1), round(org_total_height, 5), round(org_heads, 5), round(org_face_length, 5)) + logger.info("【No.%s】変換先モデル 全長: %s, 頭身: %s, 顔の大きさ: %s, Yオフセット: %s", (data_set_idx + 1), round(rep_total_height, 5), round(rep_heads, 5), round(rep_face_length, 5), data_set.camera_offset_y) return org_total_height, org_face_length, org_heads, rep_total_height, rep_face_length, rep_heads, body_ratio, head_ratio @@ -720,16 +720,18 @@ def prepare_link(self, org_model: PmxModel, rep_model: PmxModel, camera_offset_y # 元と先の両方に末端があればリンク作成 org_link = org_model.create_link_2_top_one(*link_bone_name_list) - # 頭頂実体がある場合、Yオフセット加味 - if "頭頂実体" in list(org_link.all().keys()): - org_link.get("頭頂実体").position.setY(float(org_link.get("頭頂実体").position.y()) + float(camera_offset_y)) - # 先は、判定対象ボーンとそのボーンを生成するリンクのペアを登録する rep_target_bone_name_list = [] for target_bone_name in target_bone_name_list: if target_bone_name in org_link.all().keys() and target_bone_name in rep_model.bones: # 元リンクの中にあり、かつ先ボーンの中にある場合のみ登録 - rep_links[target_bone_name] = rep_model.create_link_2_top_one(target_bone_name) + rep_link = rep_model.create_link_2_top_one(target_bone_name) + + # 頭頂実体がある場合、Yオフセット加味 + if "頭頂実体" == target_bone_name: + rep_link.get("頭頂実体").position.setY(float(rep_link.get("頭頂実体").position.y()) + float(camera_offset_y)) + + rep_links[target_bone_name] = rep_link rep_target_bone_name_list.append(target_bone_name) if len(rep_target_bone_name_list) > 0: diff --git a/src/utils/MServiceUtils.pyx b/src/utils/MServiceUtils.pyx index 4faacbb..acc2a77 100644 --- a/src/utils/MServiceUtils.pyx +++ b/src/utils/MServiceUtils.pyx @@ -452,7 +452,11 @@ cpdef MQuaternion deform_rotation(PmxModel model, VmdMotion motion, VmdBoneFrame effect_bf = motion.c_calc_bf(effect_bone.name, bf.fno, is_key=False, is_read=False, is_reset_interpolation=False) # 自身の回転量に付与親の回転量を付与率を加味して付与する - if effect_parent_bone.effect_factor < 0: + if effect_parent_bone.effect_factor == 0: + # ゼロの場合、とりあえず初期化 + logger.debug(f"モデル「{model.name}」ボーン「{effect_parent_bone.name}」の付与率がゼロ") + rot = MQuaternion() + elif effect_parent_bone.effect_factor < 0: # マイナス付与の場合、逆回転 rot = rot * (effect_bf.rotation * abs(effect_parent_bone.effect_factor)).inverted() else: diff --git a/vmdising_np64.spec b/vmdising_np64.spec index e46a455..40891fc 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,7 +26,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β64_64bit', + name='VmdSizing_5.01_β65_64bit', debug=False, bootloader_ignore_signals=False, strip=False, From f7973675849f9ce8d5511be082aaf677c2125efe Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Wed, 11 Nov 2020 08:46:44 +0900 Subject: [PATCH 33/37] =?UTF-8?q?5.01=5F=CE=B266?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VmdSizing_5.01_β66 miumiu ・足IK補正、つまさき補正で足IK親ボーンがモーションにあって、元か先モデルにない場合、  モーションの動き自体がおかしくなっているので、処理スキップするよう判定追加 ・スタンス追加補正と捩り分散で処理がスキップされた場合に、グレーアウトのままで表示するよう終了判定強化  必須ボーンは下記の通り  ・センターXZ補正 … ["センター", "上半身", "下半身", "左足IK", "右足IK", "左足", "右足"]  ・上半身補正 … ["上半身", "頭", "首", "左腕", "右腕"]  ・下半身補正 … ["下半身", "左足", "右足"]  ・足IK補正 … ["足IK", "足", "下半身"] + モーションに有効な足IK親がある場合、足IK親  ・つま先補正 … ["足IK", "つま先IK", "足首"] + モーションに有効な足IK親がある場合、足IK親  ・つま先IK補正 … ["足IK", "つま先IK", "足首", "足"]  ・肩補正 … ["頭", "首", 肩, 腕, "上半身"]  ・腕スタンス補正 … 腕IKがない事  ・センターY補正 … ["センター", "左手首", "右手首", "左足", "右足"] --- src/executor.py | 2 +- src/service/parts/StanceService.pyx | 184 ++++++++++++++++++---------- vmdising_np64.spec | 2 +- 3 files changed, 119 insertions(+), 69 deletions(-) diff --git a/src/executor.py b/src/executor.py index 6043058..07f8405 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00_β65" +VERSION_NAME = "ver5.00_β66" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/service/parts/StanceService.pyx b/src/service/parts/StanceService.pyx index 637ed06..8af35d3 100644 --- a/src/service/parts/StanceService.pyx +++ b/src/service/parts/StanceService.pyx @@ -40,6 +40,10 @@ cdef double RADIANS_8 = cos(math.radians(8)) cdef double RADIANS_12 = cos(math.radians(12)) cdef double RADIANS_15 = cos(math.radians(15)) +cdef int PROCESS_FINISH = 1 +cdef int PROCESS_SKIP = -1 +cdef int PROCESS_ERROR = 0 + cdef class StanceService(): cdef public object options @@ -63,7 +67,7 @@ cdef class StanceService(): concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) for f in futures: - if not f.result(): + if f.result() == PROCESS_ERROR: return False return True @@ -153,17 +157,23 @@ cdef class StanceService(): # for direction in ["左", "右"]: # self.spread_twist_lr(data_set_idx, direction) + total_cnt = 0 + process_cnt = 0 futures = [] with ThreadPoolExecutor(thread_name_prefix="twist{0}".format(data_set_idx), max_workers=min(5, self.options.max_workers)) as executor: for direction in ["左", "右"]: + total_cnt += 1 futures.append(executor.submit(self.spread_twist_lr, self, data_set_idx, direction, True)) concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) for f in futures: - if not f.result(): + if f.result() == PROCESS_ERROR: return False + + if f.result() == PROCESS_FINISH: + process_cnt += 1 - if self.options.now_process_ctrl: + if self.options.now_process_ctrl and process_cnt == total_cnt: self.options.now_process += 1 self.options.now_process_ctrl.write(str(self.options.now_process)) @@ -173,7 +183,7 @@ cdef class StanceService(): return True # 捩り分散左右 - cdef bint spread_twist_lr(self, int data_set_idx, str direction, bint dummy): + cdef int spread_twist_lr(self, int data_set_idx, str direction, bint dummy): cdef str arm_bone_name, arm_twist_bone_name, elbow_bone_name, wrist_twist_bone_name, wrist_bone_name, bone_name cdef MVector3D local_z_axis, arm_local_x_axis, arm_twist_local_x_axis, elbow_local_x_axis, elbow_local_y_axis, arm_local_z2y_axis, arm_local_y_axis cdef MVector3D wrist_twist_local_x_axis, wrist_local_x_axis, wrist_local_y_axis @@ -244,7 +254,7 @@ cdef class StanceService(): concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) for f in futures: if not f.result(): - return False + return PROCESS_ERROR # 捩り前のを保持 prev_twist_motion = data_set.motion.copy() @@ -257,7 +267,7 @@ cdef class StanceService(): concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) for f in futures: if not f.result(): - return False + return PROCESS_ERROR logger.info("-- %s捩り分散準備:終了【No.%s】", direction, (data_set_idx + 1)) @@ -287,7 +297,7 @@ cdef class StanceService(): for f in futures: if not f.result(): - return False + return PROCESS_ERROR logger.info("%s捩り分散後処理 - 分散中間チェック①【No.%s】", arm_bone_name, (data_set_idx + 1)) @@ -321,7 +331,7 @@ cdef class StanceService(): for f in futures: if not f.result(): - return False + return PROCESS_ERROR # # 腕系ボーンのfnos再取得 # new_fnos = data_set.motion.get_bone_fnos(arm_bone_name, arm_twist_bone_name, elbow_bone_name, wrist_twist_bone_name, wrist_bone_name) @@ -363,7 +373,7 @@ cdef class StanceService(): # concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) # for f in futures: - # if not f.result(): + # if f.result() == PROCESS_ERROR: # return False # logger.info("%s捩り分散後処理 - 円滑化【No.%s】", arm_bone_name, (data_set_idx + 1)) @@ -376,7 +386,7 @@ cdef class StanceService(): # concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) # for f in futures: - # if not f.result(): + # if f.result() == PROCESS_ERROR: # return False # logger.info("%s捩り分散後処理 - フィルタリング【No.%s】", arm_bone_name, (data_set_idx + 1)) @@ -390,7 +400,7 @@ cdef class StanceService(): # concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) # for f in futures: - # if not f.result(): + # if f.result() == PROCESS_ERROR: # return False # # 捩りボーンのbfにフィルターをかける @@ -402,7 +412,7 @@ cdef class StanceService(): # concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) # for f in futures: - # if not f.result(): + # if f.result() == PROCESS_ERROR: # return False # # 各ボーンのbfにフィルターをかける @@ -414,14 +424,16 @@ cdef class StanceService(): # concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) # for f in futures: - # if not f.result(): + # if f.result() == PROCESS_ERROR: # return False logger.info("%s捩り分散:終了【No.%s】", direction, (data_set_idx + 1)) + return PROCESS_FINISH + else: logger.info("%s捩り分散: 【No.%s】[%s]のボーン群が、作成元もしくは変換先のいずれかで足りないため、処理をスキップします。", direction, (data_set_idx + 1), ", ".join(twist_target_bones)) - return True + return PROCESS_SKIP except MKilledException as ke: raise ke except SizingException as se: @@ -1427,19 +1439,24 @@ cdef class StanceService(): # 足IK補正 cdef bint adjust_leg_ik_stance(self, int data_set_idx, MOptionsDataSet data_set): logger.info("足IK補正 【No.%s】", (data_set_idx + 1), decoration=MLogger.DECORATION_LINE) - + + total_cnt = 0 + process_cnt = 0 futures = [] - with ThreadPoolExecutor(thread_name_prefix="leg_ik{0}".format(data_set_idx), max_workers=min(2, self.options.max_workers)) as executor: + with ThreadPoolExecutor(thread_name_prefix="leg_ik{0}".format(data_set_idx), max_workers=min(5, self.options.max_workers)) as executor: for direction in ["左", "右"]: + total_cnt += 1 futures.append(executor.submit(self.adjust_leg_ik_stance_lr, self, data_set_idx, direction, 0)) - concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) for f in futures: if not f.result(): return False - - if self.options.now_process_ctrl: + + if f.result() == PROCESS_FINISH: + process_cnt += 1 + + if self.options.now_process_ctrl and process_cnt == total_cnt: self.options.now_process += 1 self.options.now_process_ctrl.write(str(self.options.now_process)) @@ -1449,7 +1466,7 @@ cdef class StanceService(): return True # 足IK補正 - cdef bint adjust_leg_ik_stance_lr(self, int data_set_idx, str direction, int dummy): + cdef int adjust_leg_ik_stance_lr(self, int data_set_idx, str direction, int dummy): cdef MOptionsDataSet data_set cdef str target_bone_name, org_ik_root_bone_name, rep_ik_root_bone_name, d_bone_name cdef int prev_sep_fno, fno, fno_idx @@ -1472,6 +1489,11 @@ cdef class StanceService(): if set(leg_ik_target_bones).issubset(data_set.org_model.bones) and set(leg_ik_target_bones).issubset(data_set.rep_model.bones): + # 足IK親がモーションにあって、かつモデルにない場合、元の位置がおかしいのでスキップ + if data_set.motion.is_active_bones("{0}足IK親".format(direction)) and ("{0}足IK親".format(direction) not in data_set.org_model.bones or "{0}足IK親".format(direction) not in data_set.rep_model.bones): + logger.info("%s足IK補正: 【No.%s】%s足IK親が、作成元もしくは変換先のいずれかで足りないため、処理をスキップします。", direction, (data_set_idx + 1), direction) + return PROCESS_SKIP + # 足IKのそれぞれでフレーム番号をチェックする prev_sep_fno = 0 if target_bone_name in data_set.motion.bones and \ @@ -1628,13 +1650,14 @@ cdef class StanceService(): prev_sep_fno = fno // 500 logger.info("%s足IK補正:終了【No.%s】", direction, (data_set_idx + 1)) + return PROCESS_FINISH else: logger.info("%s足IK補正: 【No.%s】作成元もしくは変換先の%s足IKのIKルートボーンが、「%s足」ボーンではないため、処理をスキップします。", direction, (data_set_idx + 1), direction, direction) else: logger.info("%s足IK補正: 【No.%s】[%s]のボーン群が、作成元もしくは変換先のいずれかで足りないため、処理をスキップします。", direction, (data_set_idx + 1), ", ".join(leg_ik_target_bones)) - return True + return PROCESS_SKIP except MKilledException as ke: raise ke except SizingException as se: @@ -1649,18 +1672,23 @@ cdef class StanceService(): cdef bint adjust_toe_ik_stance(self, int data_set_idx, MOptionsDataSet data_set): logger.info("つま先IK補正 【No.%s】", (data_set_idx + 1), decoration=MLogger.DECORATION_LINE) + total_cnt = 0 + process_cnt = 0 futures = [] with ThreadPoolExecutor(thread_name_prefix="toe_ik{0}".format(data_set_idx), max_workers=min(2, self.options.max_workers)) as executor: for direction in ["左", "右"]: + total_cnt += 1 futures.append(executor.submit(self.adjust_toe_ik_stance_lr, self, data_set_idx, direction, 0.0)) - concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) for f in futures: if not f.result(): return False - - if self.options.now_process_ctrl: + + if f.result() == PROCESS_FINISH: + process_cnt += 1 + + if self.options.now_process_ctrl and process_cnt == total_cnt: self.options.now_process += 1 self.options.now_process_ctrl.write(str(self.options.now_process)) @@ -1670,7 +1698,7 @@ cdef class StanceService(): return True # つま先IK補正 - cdef bint adjust_toe_ik_stance_lr(self, int data_set_idx, str direction, double dummy): + cdef int adjust_toe_ik_stance_lr(self, int data_set_idx, str direction, double dummy): cdef MOptionsDataSet data_set cdef str toe_ik_bone_name , leg_ik_bone_name , ankle_bone_name , leg_bone_name , leg_ik_parent_name cdef list toe_ik_target_bones , fnos , ik_on_fnos , d_on_fnos , org_initial_toe_trans_vs @@ -1702,10 +1730,10 @@ cdef class StanceService(): and toe_ik_bone_name in data_set.motion.bones and data_set.motion.is_active_bones(toe_ik_bone_name): # ボーンとモーションが揃ってある場合のみ補正 - if len(data_set.motion.get_bone_fnos(toe_ik_bone_name)) <= 1: + if not data_set.motion.is_active_bones(toe_ik_bone_name): # 0Fキーはあっても無視 logger.info("%sつま先IK補正: 【No.%s】処理対象キーフレがないため、処理を終了します。", direction, (data_set_idx + 1)) - return True + return PROCESS_SKIP logger.info("%s補正【No.%s】", toe_ik_bone_name, (data_set_idx + 1)) @@ -1720,7 +1748,7 @@ cdef class StanceService(): if len(fnos) <= 1: logger.info("%sつま先IK補正: 【No.%s】処理対象キーフレがないため、処理を終了します。", direction, (data_set_idx + 1)) - return True + return PROCESS_SKIP ik_on_fnos = [] d_on_fnos = [] @@ -1847,10 +1875,11 @@ cdef class StanceService(): # self.remove_unnecessary_bf_pool_parts(data_set_idx, toe_ik_bone_name, 0) logger.info("%sつま先IK補正:終了【No.%s】", direction, (data_set_idx + 1)) + return PROCESS_FINISH else: logger.info("%sつま先IK補正: 【No.%s】[%s]のボーン群が、作成元もしくは変換先のいずれかで足りないため、処理をスキップします。", direction, (data_set_idx + 1), ", ".join(toe_ik_target_bones)) - return True + return PROCESS_SKIP except MKilledException as ke: raise ke except SizingException as se: @@ -1865,18 +1894,23 @@ cdef class StanceService(): cdef bint adjust_toe_stance(self, int data_set_idx, MOptionsDataSet data_set): logger.info("つま先補正 【No.%s】", (data_set_idx + 1), decoration=MLogger.DECORATION_LINE) + total_cnt = 0 + process_cnt = 0 futures = [] with ThreadPoolExecutor(thread_name_prefix="toe{0}".format(data_set_idx), max_workers=min(2, self.options.max_workers)) as executor: for direction in ["左", "右"]: + total_cnt += 1 futures.append(executor.submit(self.adjust_toe_stance_lr, self, data_set_idx, direction, "")) - concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) for f in futures: if not f.result(): return False - - if self.options.now_process_ctrl: + + if f.result() == PROCESS_FINISH: + process_cnt += 1 + + if self.options.now_process_ctrl and process_cnt == total_cnt: self.options.now_process += 1 self.options.now_process_ctrl.write(str(self.options.now_process)) @@ -1886,7 +1920,7 @@ cdef class StanceService(): return True # つま先補正 - cdef bint adjust_toe_stance_lr(self, int data_set_idx, str direction, str dummy): + cdef int adjust_toe_stance_lr(self, int data_set_idx, str direction, str dummy): cdef double adjust_sole_y, adjust_toe_y, org_sole_diff, org_toe_diff, org_toe_limit, rep_sole_diff, rep_toe_diff, rep_toe_limit, sole_diff, toe_diff, toe_limit_ratio cdef MOptionsDataSet data_set cdef list fnos, toe_target_bones @@ -1903,6 +1937,11 @@ cdef class StanceService(): # つま先調整に必要なボーン群 toe_target_bones = ["{0}足IK".format(direction), "{0}つま先IK".format(direction), "{0}足首".format(direction), "{0}つま先実体".format(direction), "{0}足底実体".format(direction)] + # 足IK親がモーションにあって、かつモデルにない場合、元の位置がおかしいのでスキップ + if data_set.motion.is_active_bones("{0}足IK親".format(direction)) and ("{0}足IK親".format(direction) not in data_set.org_model.bones or "{0}足IK親".format(direction) not in data_set.rep_model.bones): + logger.info("%sつま先補正: 【No.%s】%s足IK親が、作成元もしくは変換先のいずれかで足りないため、処理をスキップします。", direction, (data_set_idx + 1), direction) + return PROCESS_SKIP + if set(toe_target_bones).issubset(data_set.org_model.bones) and set(toe_target_bones).issubset(data_set.rep_model.bones): org_toe_links = data_set.org_model.create_link_2_top_one("{0}つま先実体".format(direction)) rep_toe_links = data_set.rep_model.create_link_2_top_one("{0}つま先実体".format(direction)) @@ -1980,10 +2019,11 @@ cdef class StanceService(): prev_sep_fno = fno // 500 logger.info("%sつま先補正:終了【No.%s】", direction, (data_set_idx + 1)) + return PROCESS_FINISH else: logger.info("%sつま先補正: 【No.%s】[%s]のボーン群が、作成元もしくは変換先のいずれかで足りないため、処理をスキップします。", direction, (data_set_idx + 1), ", ".join(toe_target_bones)) - return True + return PROCESS_SKIP except MKilledException as ke: raise ke except SizingException as se: @@ -2069,15 +2109,16 @@ cdef class StanceService(): prev_fno = fno // 500 logger.info("センターXZ補正: 終了【No.%s】", (data_set_idx + 1)) - else: - logger.info("センターXZ補正: 【No.%s】[%s]のボーン群が、作成元もしくは変換先のいずれかで足りないため、処理をスキップします。", (data_set_idx + 1), ", ".join(center_target_bones)) - if self.options.now_process_ctrl: - self.options.now_process += 1 - self.options.now_process_ctrl.write(str(self.options.now_process)) + if self.options.now_process_ctrl: + self.options.now_process += 1 + self.options.now_process_ctrl.write(str(self.options.now_process)) - proccess_key = "【No.{0}】{1}({2})".format(data_set_idx + 1, os.path.basename(data_set.motion.path), data_set.rep_model.name) - self.options.tree_process_dict[proccess_key]["スタンス追加補正"]["センターXZ補正"] = True + proccess_key = "【No.{0}】{1}({2})".format(data_set_idx + 1, os.path.basename(data_set.motion.path), data_set.rep_model.name) + self.options.tree_process_dict[proccess_key]["スタンス追加補正"]["センターXZ補正"] = True + + else: + logger.info("センターXZ補正: 【No.%s】[%s]のボーン群が、作成元もしくは変換先のいずれかで足りないため、処理をスキップします。", (data_set_idx + 1), ", ".join(center_target_bones)) return True @@ -2165,15 +2206,16 @@ cdef class StanceService(): prev_fno = fno // 500 logger.info("センターY補正: 終了【No.%s】", (data_set_idx + 1)) - else: - logger.info("センターY補正: 【No.%s】[%s]のボーン群が、作成元もしくは変換先のいずれかで足りないため、処理をスキップします。", (data_set_idx + 1), ", ".join(center_target_bones)) - if self.options.now_process_ctrl: - self.options.now_process += 1 - self.options.now_process_ctrl.write(str(self.options.now_process)) + if self.options.now_process_ctrl: + self.options.now_process += 1 + self.options.now_process_ctrl.write(str(self.options.now_process)) - proccess_key = "【No.{0}】{1}({2})".format(data_set_idx + 1, os.path.basename(data_set.motion.path), data_set.rep_model.name) - self.options.tree_process_dict[proccess_key]["スタンス追加補正"]["センターY補正"] = True + proccess_key = "【No.{0}】{1}({2})".format(data_set_idx + 1, os.path.basename(data_set.motion.path), data_set.rep_model.name) + self.options.tree_process_dict[proccess_key]["スタンス追加補正"]["センターY補正"] = True + + else: + logger.info("センターY補正: 【No.%s】[%s]のボーン群が、作成元もしくは変換先のいずれかで足りないため、処理をスキップします。", (data_set_idx + 1), ", ".join(center_target_bones)) return True @@ -2653,15 +2695,16 @@ cdef class StanceService(): self.adjust_rotation_by_parent(data_set_idx, data_set, "右腕", "上半身2") logger.info("上半身2補正: 終了【No.%s】", (data_set_idx + 1)) - else: - logger.info("上半身補正: 【No.%s】[%s]のボーン群が、作成元もしくは変換先のいずれかで足りないため、処理をスキップします。", (data_set_idx + 1), ", ".join(upper_target_bones)) - if self.options.now_process_ctrl: - self.options.now_process += 1 - self.options.now_process_ctrl.write(str(self.options.now_process)) + if self.options.now_process_ctrl: + self.options.now_process += 1 + self.options.now_process_ctrl.write(str(self.options.now_process)) - proccess_key = "【No.{0}】{1}({2})".format(data_set_idx + 1, os.path.basename(data_set.motion.path), data_set.rep_model.name) - self.options.tree_process_dict[proccess_key]["スタンス追加補正"]["上半身補正"] = True + proccess_key = "【No.{0}】{1}({2})".format(data_set_idx + 1, os.path.basename(data_set.motion.path), data_set.rep_model.name) + self.options.tree_process_dict[proccess_key]["スタンス追加補正"]["上半身補正"] = True + + else: + logger.info("上半身補正: 【No.%s】[%s]のボーン群が、作成元もしくは変換先のいずれかで足りないため、処理をスキップします。", (data_set_idx + 1), ", ".join(upper_target_bones)) return True @@ -2786,16 +2829,16 @@ cdef class StanceService(): self.adjust_rotation_by_parent_ik(data_set_idx, data_set, "右足", "下半身", "右足IK") logger.info("下半身補正: 終了【No.%s】", (data_set_idx + 1)) + + if self.options.now_process_ctrl: + self.options.now_process += 1 + self.options.now_process_ctrl.write(str(self.options.now_process)) + + proccess_key = "【No.{0}】{1}({2})".format(data_set_idx + 1, os.path.basename(data_set.motion.path), data_set.rep_model.name) + self.options.tree_process_dict[proccess_key]["スタンス追加補正"]["下半身補正"] = True else: logger.info("下半身補正: 【No.%s】[%s]のボーン群が、作成元もしくは変換先のいずれかで足りないため、処理をスキップします。", (data_set_idx + 1), ", ".join(lower_target_bones)) - if self.options.now_process_ctrl: - self.options.now_process += 1 - self.options.now_process_ctrl.write(str(self.options.now_process)) - - proccess_key = "【No.{0}】{1}({2})".format(data_set_idx + 1, os.path.basename(data_set.motion.path), data_set.rep_model.name) - self.options.tree_process_dict[proccess_key]["スタンス追加補正"]["下半身補正"] = True - return True # 体幹スタンス補正 @@ -2926,18 +2969,23 @@ cdef class StanceService(): cdef bint adjust_shoulder_stance(self, int data_set_idx, MOptionsDataSet data_set): logger.info("肩補正 【No.%s】", (data_set_idx + 1), decoration=MLogger.DECORATION_LINE) + total_cnt = 0 + process_cnt = 0 futures = [] with ThreadPoolExecutor(thread_name_prefix="shoulder{0}".format(data_set_idx), max_workers=min(2, self.options.max_workers)) as executor: for direction in ["左", "右"]: + total_cnt += 1 futures.append(executor.submit(self.adjust_shoulder_stance_lr, self, data_set_idx, "{0}肩P".format(direction), "{0}肩".format(direction), "{0}腕".format(direction))) - concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) for f in futures: if not f.result(): return False - - if self.options.now_process_ctrl: + + if f.result() == PROCESS_FINISH: + process_cnt += 1 + + if self.options.now_process_ctrl and process_cnt == total_cnt: self.options.now_process += 1 self.options.now_process_ctrl.write(str(self.options.now_process)) @@ -2947,7 +2995,7 @@ cdef class StanceService(): return True # 肩補正左右 - cdef bint adjust_shoulder_stance_lr(self, int data_set_idx, str shoulder_p_name, str shoulder_name, str arm_name): + cdef int adjust_shoulder_stance_lr(self, int data_set_idx, str shoulder_p_name, str shoulder_name, str arm_name): cdef MOptionsDataSet data_set cdef double dot cdef list shoulder_target_bones @@ -2990,10 +3038,12 @@ cdef class StanceService(): else: logger.warning("%sの初期スタンスの角度が大きく違うため、肩補正の結果がおかしくなる可能性があります【No.%s】", shoulder_name, (data_set_idx + 1)) self.adjust_shoulder_stance_far(data_set_idx, shoulder_p_name, shoulder_name, arm_name, 0, is_shoulder_p) + + return PROCESS_FINISH else: logger.info("%s補正: 【No.%s】[%s]のボーン群が、作成元もしくは変換先のいずれかで足りないため、処理をスキップします。", shoulder_name, (data_set_idx + 1), ", ".join(shoulder_target_bones)) - return True + return PROCESS_SKIP except MKilledException as ke: raise ke except SizingException as se: diff --git a/vmdising_np64.spec b/vmdising_np64.spec index 40891fc..7700fe3 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,7 +26,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β65_64bit', + name='VmdSizing_5.01_β66_64bit', debug=False, bootloader_ignore_signals=False, strip=False, From b7f53ca5d7713bdc73a12094a9849141c23265d2 Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Sat, 14 Nov 2020 06:53:03 +0900 Subject: [PATCH 34/37] =?UTF-8?q?5.01=5F=CE=B267?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VmdSizing_5.01_β67 miumiu ・VMD出力  ・IKonoffの出力条件に「カメラデータがない場合」を追加   ※ボーン追従情報がこのエリアに入ってる模様    MMDではボーン追従情報が登録されたVMDを読み込んだ時に追従情報が無視されるので、出力しないで問題なし --- src/executor.py | 2 +- src/mmd/VmdWriter.py | 8 +++++--- vmdising_np64.spec | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/executor.py b/src/executor.py index 07f8405..dae0326 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00_β66" +VERSION_NAME = "ver5.00_β67" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/mmd/VmdWriter.py b/src/mmd/VmdWriter.py index 6022f2a..296b6f7 100644 --- a/src/mmd/VmdWriter.py +++ b/src/mmd/VmdWriter.py @@ -54,8 +54,10 @@ def write(self): fout.write(struct.pack(' Date: Wed, 18 Nov 2020 01:01:51 +0900 Subject: [PATCH 35/37] =?UTF-8?q?5.01=5F=CE=B268?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VmdSizing_5.01_β68 miumiu ・不要キー削除処理をモーションサポーターと合わせた  ※サイジングで同処理は実行されない --- src/executor.py | 2 +- src/mmd/VmdData.pyx | 262 ++++++++++++++++++++----------------- src/utils/MBezierUtils.pxd | 3 +- src/utils/MBezierUtils.pyx | 197 ++++------------------------ vmdising_np64.spec | 2 +- 5 files changed, 173 insertions(+), 293 deletions(-) diff --git a/src/executor.py b/src/executor.py index dae0326..7a40b59 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.00_β67" +VERSION_NAME = "ver5.01_β68" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/mmd/VmdData.pyx b/src/mmd/VmdData.pyx index f8fa138..0a4a347 100644 --- a/src/mmd/VmdData.pyx +++ b/src/mmd/VmdData.pyx @@ -565,132 +565,136 @@ cdef class VmdMotion: del self.bones[bone_name][fno] # 指定ボーンの不要キーを削除する - def remove_unnecessary_bf(self, data_set_no: int, bone_name: str, is_rot: bool, is_mov: bool, offset=0, start_fno=-1, end_fno=-1, is_show_log=True, is_force=False): - prev_sep_fno = 0 - - # キーフレを取得する - suffix = "" - if start_fno < 0 and end_fno < 0: - # 範囲指定がない場合、全範囲 - fnos = self.get_bone_fnos(bone_name) - else: - # 範囲指定がある場合はその範囲内だけ - fnos = self.get_bone_fnos(bone_name, start_fno=start_fno, end_fno=end_fno) - suffix = f"({start_fno}-{end_fno})" - logger.debug("remove_unnecessary_bf prev: %s, %s", bone_name, len(fnos)) - - if len(fnos) > 2: - sfno = fnos[0] # 開始フレーム番号 - fno = fnos[1] # 次のフレーム番号 - fill_bfs = [] - for fidx, fno in enumerate(fnos): - prev_bf = self.calc_bf(bone_name, sfno) # 繋ぐ元のbf - now_bf = self.calc_bf(bone_name, fno) # 繋ぐ対象のbf - next_bf = self.calc_bf(bone_name, fno + 1) # 繋ぐ先のbf - is_next_key = next_bf.key # nextの有効有無 - - # 一旦登録 - self.regist_bf(now_bf, bone_name, fno) - - # 読み込みキーではない場合、結合を試す - logger.test("now: %s", now_bf) - - if (not now_bf.read or is_force) and fidx > 0: - # 現在キーを追加 - fill_bfs.append(now_bf) - - if self.join_bf(prev_bf, fill_bfs, next_bf, is_rot, is_mov, offset): - # 全ての補間曲線が繋ぐのに成功した場合、繋ぐ - logger.debug("f: %s, %s, ○補間曲線結合", fno, bone_name) - - # nowキーを物理的に削除 - if fno in self.bones[bone_name]: - del self.bones[bone_name][fno] + # 変曲点を求める + # https://teratail.com/questions/162391 + def remove_unnecessary_bf(self, data_set_no: int, bone_name: str, is_rot: bint, is_mov: bint, \ + offset=0, rot_diff_limit=0.1, mov_diff_limit=0.1, start_fno=-1, end_fno=-1, is_show_log=True, is_force=False): + self.c_remove_unnecessary_bf(data_set_no, bone_name, is_rot, is_mov, offset, rot_diff_limit, mov_diff_limit, start_fno, end_fno, is_show_log, is_force) + + + # def remove_unnecessary_bf(self, data_set_no: int, bone_name: str, is_rot: bool, is_mov: bool, offset=0, start_fno=-1, end_fno=-1, is_show_log=True, is_force=False): + # prev_sep_fno = 0 + + # # キーフレを取得する + # if start_fno < 0 and end_fno < 0: + # # 範囲指定がない場合、全範囲 + # fnos = self.get_bone_fnos(bone_name) + # else: + # # 範囲指定がある場合はその範囲内だけ + # fnos = self.get_bone_fnos(bone_name, start_fno=start_fno, end_fno=end_fno) + # logger.debug("remove_unnecessary_bf prev: %s, %s", bone_name, len(fnos)) + + # if len(fnos) > 2: + # sfno = fnos[0] # 開始フレーム番号 + # fno = fnos[1] # 次のフレーム番号 + # fill_bfs = [] + # for fidx, fno in enumerate(fnos): + # prev_bf = self.calc_bf(bone_name, sfno) # 繋ぐ元のbf + # now_bf = self.calc_bf(bone_name, fno) # 繋ぐ対象のbf + # next_bf = self.calc_bf(bone_name, fno + 1) # 繋ぐ先のbf + # is_next_key = next_bf.key # nextの有効有無 + + # # 一旦登録 + # self.regist_bf(now_bf, bone_name, fno) + + # # 読み込みキーではない場合、結合を試す + # logger.test("now: %s", now_bf) + + # if (not now_bf.read or is_force) and fidx > 0: + # # 現在キーを追加 + # fill_bfs.append(now_bf) + + # if self.join_bf(prev_bf, fill_bfs, next_bf, is_rot, is_mov, offset): + # # 全ての補間曲線が繋ぐのに成功した場合、繋ぐ + # logger.debug("f: %s, %s, ○補間曲線結合", fno, bone_name) + + # # nowキーを物理的に削除 + # if fno in self.bones[bone_name]: + # del self.bones[bone_name][fno] - # nextキーをキーの有効有無は問わずに登録 - self.regist_bf(next_bf, bone_name, fno + 1) - self.bones[bone_name][fno + 1].key = is_next_key - else: - logger.debug("f: %s, %s, ×補間曲線結合失敗", fno, bone_name) - # どれか失敗してたら、そのまま残す - - # nowキーを有効にする - now_bf.key = True - - sfno = fno # 開始を現在フレーム - fill_bfs = [] # 中間キーをクリア - else: - logger.debug("f: %s, %s, ▲読み込みキー", fno, bone_name) - # 読み込み時のキーである場合、強制的に残す - sfno = fno # 開始を現在フレーム - fill_bfs = [] # 中間キーをクリア - - if fno // 100 > prev_sep_fno: - if data_set_no == 0: - logger.info("-- %sフレーム目:終了(%s%)【不要キー削除%s - %s】", fno, round((fno / fnos[-1]) * 100, 3), suffix, bone_name) - else: - logger.info("-- %sフレーム目:終了(%s%)【No.%s - 不要キー削除%s - %s】", fno, round((fno / fnos[-1]) * 100, 3), data_set_no, suffix, bone_name) - - prev_sep_fno = fno // 100 - - if start_fno < 0 and end_fno < 0: - # 範囲指定がない場合、全範囲 - active_fnos = self.get_bone_fnos(bone_name) - else: - # 範囲指定がある場合はその範囲内だけ - active_fnos = self.get_bone_fnos(bone_name, start_fno=start_fno, end_fno=end_fno) - - logger.debug("remove_unnecessary_bf after: %s, %s, all: %s", bone_name, active_fnos, len(fnos)) - - # 補間曲線込みでbfを結合できる場合、結合する - def join_bf(self, prev_bf: VmdBoneFrame, fill_bfs: list, next_bf: VmdBoneFrame, is_rot: bool, is_mov: bool, offset=0): - rot_values = [] - x_values = [] - y_values = [] - z_values = [] - - if is_rot: - rot_values = np.array([prev_bf.rotation.toDegree() * np.sign(prev_bf.rotation.x())] \ - + [bf.rotation.toDegree() * np.sign(bf.rotation.x()) for bf in fill_bfs] \ - + [next_bf.rotation.toDegree() * np.sign(next_bf.rotation.x())]) - (prev_bf.rotation.toDegree() * np.sign(next_bf.rotation.x())) + # # nextキーをキーの有効有無は問わずに登録 + # self.regist_bf(next_bf, bone_name, fno + 1) + # self.bones[bone_name][fno + 1].key = is_next_key + # else: + # logger.debug("f: %s, %s, ×補間曲線結合失敗", fno, bone_name) + # # どれか失敗してたら、そのまま残す + + # # nowキーを有効にする + # now_bf.key = True + + # sfno = fno # 開始を現在フレーム + # fill_bfs = [] # 中間キーをクリア + # else: + # logger.debug("f: %s, %s, ▲読み込みキー", fno, bone_name) + # # 読み込み時のキーである場合、強制的に残す + # sfno = fno # 開始を現在フレーム + # fill_bfs = [] # 中間キーをクリア + + # if fno // 100 > prev_sep_fno: + # if data_set_no == 0: + # logger.info("-- %sフレーム目:終了(%s%)【不要キー削除 - %s】", fno, round((fno / fnos[-1]) * 100, 3), bone_name) + # else: + # logger.info("-- %sフレーム目:終了(%s%)【No.%s - 不要キー削除 - %s】", fno, round((fno / fnos[-1]) * 100, 3), data_set_no, bone_name) + + # prev_sep_fno = fno // 100 + + # if start_fno < 0 and end_fno < 0: + # # 範囲指定がない場合、全範囲 + # active_fnos = self.get_bone_fnos(bone_name) + # else: + # # 範囲指定がある場合はその範囲内だけ + # active_fnos = self.get_bone_fnos(bone_name, start_fno=start_fno, end_fno=end_fno) + + # logger.debug("remove_unnecessary_bf after: %s, %s, all: %s", bone_name, active_fnos, len(fnos)) + + # # 補間曲線込みでbfを結合できる場合、結合する + # def join_bf(self, prev_bf: VmdBoneFrame, fill_bfs: list, next_bf: VmdBoneFrame, is_rot: bool, is_mov: bool, offset=0): + # rot_values = [] + # x_values = [] + # y_values = [] + # z_values = [] + + # if is_rot: + # rot_values = np.array([prev_bf.rotation.toDegree() * np.sign(prev_bf.rotation.x())] \ + # + [bf.rotation.toDegree() * np.sign(bf.rotation.x()) for bf in fill_bfs] \ + # + [next_bf.rotation.toDegree() * np.sign(next_bf.rotation.x())]) - (prev_bf.rotation.toDegree() * np.sign(next_bf.rotation.x())) - logger.test("f: %s, %s, rot_values: %s", prev_bf.fno, prev_bf.name, rot_values) - - if is_mov: - # X ------------ - x_values = np.array([prev_bf.position.x()] + [bf.position.x() for bf in fill_bfs] + [next_bf.position.x()]) - prev_bf.position.x() + # logger.test("f: %s, %s, rot_values: %s", prev_bf.fno, prev_bf.name, rot_values) - # Y ----------- - y_values = np.array([prev_bf.position.y()] + [bf.position.y() for bf in fill_bfs] + [next_bf.position.y()]) - prev_bf.position.y() + # if is_mov: + # # X ------------ + # x_values = np.array([prev_bf.position.x()] + [bf.position.x() for bf in fill_bfs] + [next_bf.position.x()]) - prev_bf.position.x() - # Z ----------- - z_values = np.array([prev_bf.position.z()] + [bf.position.z() for bf in fill_bfs] + [next_bf.position.z()]) - prev_bf.position.z() + # # Y ----------- + # y_values = np.array([prev_bf.position.y()] + [bf.position.y() for bf in fill_bfs] + [next_bf.position.y()]) - prev_bf.position.y() - logger.test("f: %s, %s, x_values: %s", prev_bf.fno, prev_bf.name, x_values) - logger.test("f: %s, %s, y_values: %s", prev_bf.fno, prev_bf.name, y_values) - logger.test("f: %s, %s, z_values: %s", prev_bf.fno, prev_bf.name, z_values) + # # Z ----------- + # z_values = np.array([prev_bf.position.z()] + [bf.position.z() for bf in fill_bfs] + [next_bf.position.z()]) - prev_bf.position.z() - # 結合したベジェ曲線 - joined_rot_bzs = MBezierUtils.join_value_2_bezier(fill_bfs[-1].fno, next_bf.name, list(rot_values), offset=offset, diff_limit=0.1) if is_rot else True - joined_x_bzs = MBezierUtils.join_value_2_bezier(fill_bfs[-1].fno, next_bf.name, list(x_values), offset=offset, diff_limit=0.01) if is_mov else True - joined_y_bzs = MBezierUtils.join_value_2_bezier(fill_bfs[-1].fno, next_bf.name, list(y_values), offset=offset, diff_limit=0.01) if is_mov else True - joined_z_bzs = MBezierUtils.join_value_2_bezier(fill_bfs[-1].fno, next_bf.name, list(z_values), offset=offset, diff_limit=0.01) if is_mov else True + # logger.test("f: %s, %s, x_values: %s", prev_bf.fno, prev_bf.name, x_values) + # logger.test("f: %s, %s, y_values: %s", prev_bf.fno, prev_bf.name, y_values) + # logger.test("f: %s, %s, z_values: %s", prev_bf.fno, prev_bf.name, z_values) - if joined_rot_bzs and joined_x_bzs and joined_y_bzs and joined_z_bzs: - # 結合できた場合、補間曲線をnextに設定 - if is_rot: - self.reset_interpolation_parts(prev_bf.name, next_bf, joined_rot_bzs, MBezierUtils.R_x1_idxs, MBezierUtils.R_y1_idxs, MBezierUtils.R_x2_idxs, MBezierUtils.R_y2_idxs) + # # 結合したベジェ曲線 + # joined_rot_bzs = MBezierUtils.join_value_2_bezier(fill_bfs[-1].fno, next_bf.name, list(rot_values), offset=offset, diff_limit=0.1) if is_rot else True + # joined_x_bzs = MBezierUtils.join_value_2_bezier(fill_bfs[-1].fno, next_bf.name, list(x_values), offset=offset, diff_limit=0.01) if is_mov else True + # joined_y_bzs = MBezierUtils.join_value_2_bezier(fill_bfs[-1].fno, next_bf.name, list(y_values), offset=offset, diff_limit=0.01) if is_mov else True + # joined_z_bzs = MBezierUtils.join_value_2_bezier(fill_bfs[-1].fno, next_bf.name, list(z_values), offset=offset, diff_limit=0.01) if is_mov else True - if is_mov: - self.reset_interpolation_parts(prev_bf.name, next_bf, joined_x_bzs, MBezierUtils.MX_x1_idxs, MBezierUtils.MX_y1_idxs, MBezierUtils.MX_x2_idxs, MBezierUtils.MX_y2_idxs) - self.reset_interpolation_parts(prev_bf.name, next_bf, joined_y_bzs, MBezierUtils.MY_x1_idxs, MBezierUtils.MY_y1_idxs, MBezierUtils.MY_x2_idxs, MBezierUtils.MY_y2_idxs) - self.reset_interpolation_parts(prev_bf.name, next_bf, joined_z_bzs, MBezierUtils.MZ_x1_idxs, MBezierUtils.MZ_y1_idxs, MBezierUtils.MZ_x2_idxs, MBezierUtils.MZ_y2_idxs) + # if joined_rot_bzs and joined_x_bzs and joined_y_bzs and joined_z_bzs: + # # 結合できた場合、補間曲線をnextに設定 + # if is_rot: + # self.reset_interpolation_parts(prev_bf.name, next_bf, joined_rot_bzs, MBezierUtils.R_x1_idxs, MBezierUtils.R_y1_idxs, MBezierUtils.R_x2_idxs, MBezierUtils.R_y2_idxs) - return True + # if is_mov: + # self.reset_interpolation_parts(prev_bf.name, next_bf, joined_x_bzs, MBezierUtils.MX_x1_idxs, MBezierUtils.MX_y1_idxs, MBezierUtils.MX_x2_idxs, MBezierUtils.MX_y2_idxs) + # self.reset_interpolation_parts(prev_bf.name, next_bf, joined_y_bzs, MBezierUtils.MY_x1_idxs, MBezierUtils.MY_y1_idxs, MBezierUtils.MY_x2_idxs, MBezierUtils.MY_y2_idxs) + # self.reset_interpolation_parts(prev_bf.name, next_bf, joined_z_bzs, MBezierUtils.MZ_x1_idxs, MBezierUtils.MZ_y1_idxs, MBezierUtils.MZ_x2_idxs, MBezierUtils.MZ_y2_idxs) - # 結合できなかった場合、False - return False + # return True + # # 結合できなかった場合、False + # return False # 指定ボーンの不要キーを削除する # 変曲点を求める @@ -711,6 +715,28 @@ cdef class VmdMotion: if len(fnos) <= 1: return + + cdef int f + cdef VmdBoneFrame bf = None + cdef VmdBoneFrame prev_bf = None + + rot_vs = [] + mov_vs = [] + prev_bf = self.c_calc_bf(bone_name, fnos[0], is_key=False, is_read=False, is_reset_interpolation=False) + + for f in fnos[1:]: + bf = self.c_calc_bf(bone_name, f, is_key=False, is_read=False, is_reset_interpolation=False) + + rot_vs.append(bf.rotation.calcTheata(prev_bf.rotation)) + mov_vs.append(bf.position.distanceToPoint(prev_bf.position)) + + prev_bf = bf + + # 差異がないキーを除去する + if (not is_rot or (is_rot and sum(rot_vs) < 0.001)) and (not is_mov or (is_mov and sum(mov_vs) < 0.001)): + for f in range(1, fnos[-1] + 1): + if f in self.bones[bone_name]: + del self.bones[bone_name][f] cdef int fno = fnos[0] + 1 cdef int start_fno = fnos[0] @@ -719,10 +745,7 @@ cdef class VmdMotion: cdef list mx_values = [0] cdef list my_values = [0] cdef list mz_values = [0] - cdef VmdBoneFrame bf = None - cdef VmdBoneFrame prev_bf = None cdef VmdBoneFrame next_bf = None - cdef int f cdef list rot_indices = [] cdef list mx_indices = [] cdef list my_indices = [] @@ -745,6 +768,7 @@ cdef class VmdMotion: cdef bint is_inflection = False # cdef list joined_rot_bzs, joined_mx_bzs, joined_my_bzs, joined_mz_bzs, rot_inflection, mx_inflection, my_inflection, mz_inflection + # 不要キー削除処理 while fno <= fnos[-1]: is_inflection = False inflection_fno = start_fno @@ -779,7 +803,7 @@ cdef class VmdMotion: (joined_mz_bzs, mz_inflection) = MBezierUtils.join_value_2_bezier(fno, bone_name, mz_values, \ offset=offset, diff_limit=mov_diff_limit) if is_mov else (True, []) - if joined_rot_bzs and joined_mx_bzs and joined_my_bzs and joined_mz_bzs: + if joined_rot_bzs and joined_mx_bzs and joined_my_bzs and joined_mz_bzs and bf.rotation.toEulerAngles().distanceToPoint(prev_bf.rotation.toEulerAngles()) < 90: next_bf = self.c_calc_bf(bone_name, fno, is_key=False, is_read=False, is_reset_interpolation=False) # 結合できた場合、補間曲線をnextに設定 diff --git a/src/utils/MBezierUtils.pxd b/src/utils/MBezierUtils.pxd index 3f1ea64..9c163da 100644 --- a/src/utils/MBezierUtils.pxd +++ b/src/utils/MBezierUtils.pxd @@ -7,7 +7,7 @@ from module.MMath cimport MRect, MVector2D, MVector3D, MVector4D, MQuaternion, M cdef double calc_catmull_rom_one_point(double x, double v0, double v1, double v2, double v3) except? -1 -cpdef np.ndarray calc_value_from_catmullrom(str bone_name, list fnos, list values) +cdef np.ndarray calc_value_from_catmullrom(str bone_name, list fnos, list values) cdef bint fit_bezier_mmd(list bzs) @@ -30,4 +30,3 @@ cdef MVector2D round_bezier_mmd(MVector2D target) cdef int round_integer(double t) - diff --git a/src/utils/MBezierUtils.pyx b/src/utils/MBezierUtils.pyx index ea5d55f..2e5d380 100644 --- a/src/utils/MBezierUtils.pyx +++ b/src/utils/MBezierUtils.pyx @@ -6,7 +6,6 @@ import numpy as np cimport numpy as np import bezier cimport bezier._curve -cimport bezier._helpers logger = MLogger(__name__, level=1) @@ -78,7 +77,7 @@ cdef double calc_catmull_rom_one_point(double x, double v0, double v1, double v2 # 指定したすべての値をカトマル曲線として計算する -cpdef np.ndarray calc_value_from_catmullrom(str bone_name, list fnos, list values): +cdef np.ndarray calc_value_from_catmullrom(str bone_name, list fnos, list values): cdef np.ndarray[np.float_t, ndim=1] y_intpol cdef list prev_list, next_list cdef int fidx, sfno, efno, res @@ -137,140 +136,15 @@ cpdef np.ndarray calc_value_from_catmullrom(str bone_name, list fnos, list value # 指定したすべての値を通るカトマル曲線からベジェ曲線を計算し、MMD補間曲線範囲内に収められた場合、そのベジェ曲線を返す def join_value_2_bezier(fno: int, bone_name: str, values: list, offset=0, diff_limit=0.01): - # return_tuple = c_join_value_2_bezier(fno, bone_name, values, offset, diff_limit) - # return return_tuple[0], return_tuple[1] - - if np.isclose(np.max(np.array(values)), np.min(np.array(values)), atol=1e-3) or len(values) <= 2: - # すべてがだいたい同じ値(最小と最大が同じ値)か次数が1の場合、線形補間 - return LINEAR_MMD_INTERPOLATION - - try: - # Xは次数(フレーム数)分移動 - xs = np.arange(0, len(values)) - # YはXの移動分を許容範囲とする - ys = values + xs[-1] - - # カトマル曲線をベジェ曲線に変換する - bz_x, bz_y = convert_catmullrom_2_bezier(np.concatenate([[None], xs, [None]]), np.concatenate([[None], ys, [None]])) - logger.test("bz_x: %s", bz_x) - logger.test("bz_y: %s", bz_y) - - if len(bz_x) == 0: - # 始点と終点が指定されていて、カトマル曲線が描けなかった場合、線形補間 - return LINEAR_MMD_INTERPOLATION - - # 次数 - degree = len(bz_x) - 1 - logger.test("degree: %s", degree) - - # すべての制御点を加味したベジェ曲線 - full_curve = bezier.Curve(np.asfortranarray([bz_x, bz_y]), degree=degree) - - if degree < 3: - # 3次未満の場合、3次まで次数を増やす - joined_curve = full_curve.elevate() - for _ in range(1, 3 - degree): - joined_curve = joined_curve.elevate() - elif degree == 3: - # 3次の場合、そのままベジェ曲線をMMD用に補間 - joined_curve = full_curve - elif degree > 3: - # 3次より多い場合、次数を減らす - - reduced_curve_list = [] - bz_x = full_curve.nodes[0] - bz_y = full_curve.nodes[1] - logger.test("START bz_x: %s, bz_y: %s", bz_x, bz_y) - - # 3次になるまでベジェ曲線を繋いで減らしていく - while len(bz_x) > 4: - reduced_curve_list = [] - - for n in range(0, degree + 1, 5): - reduce_bz_x = bz_x[n:n + 5] - reduce_bz_y = bz_y[n:n + 5] - logger.test("n: %s, reduce_bz_x: %s, reduce_bz_y: %s", n, reduce_bz_x, reduce_bz_y) - reduced_curve = bezier.Curve(np.asfortranarray([reduce_bz_x, reduce_bz_y]), degree=(len(reduce_bz_x) - 1)) - - # 次数がある場合、減らす - if (len(reduce_bz_x) - 1) > 1: - reduced_curve = reduced_curve.reduce_() - - logger.test("n: %s, nodes: %s", n, reduced_curve.nodes) - - # リストに追加 - reduced_curve_list.append(reduced_curve) - - bz_x = [] - bz_y = [] - - for reduced_curve in reduced_curve_list: - bz_x = np.append(bz_x, reduced_curve.nodes[0]) - bz_y = np.append(bz_y, reduced_curve.nodes[1]) - - logger.test("NEXT bz_x: %s, bz_y: %s", bz_x, bz_y) - - logger.test("FINISH bz_x: %s, bz_y: %s", bz_x, bz_y) - - # bz_x = [full_curve.nodes[0][0]] + list(bz_x) + [full_curve.nodes[0][-1]] - # bz_y = [full_curve.nodes[0][0]] + list(bz_y) + [full_curve.nodes[0][-1]] - - joined_curve = bezier.Curve(np.asfortranarray([bz_x, bz_y]), degree=(len(bz_x) - 1)) - - logger.test("joined_curve: %s", joined_curve.nodes) - - # 全体のキーフレ - bezier_x = np.arange(0, len(values))[1:-1] - - # 元の2つのベジェ曲線との交点を取得する - full_ys = intersect_by_x(full_curve, bezier_x) - logger.test("f: %s, %s, full_ys: %s", fno, bone_name, full_ys) - - # 次数を減らしたベジェ曲線との交点を取得する - reduced_ys = intersect_by_x(joined_curve, bezier_x) - logger.test("f: %s, %s, reduced_ys: %s", fno, bone_name, reduced_ys) - - # 交点の差を取得する - diff_ys = np.array(full_ys) - np.array(reduced_ys) - - # 差が大きい箇所をピックアップする - diff_large = np.where(np.abs(diff_ys) > (diff_limit * (offset + 1)), 1, 0) - - # 差が一定未満である場合、ベジェ曲線をMMD補間曲線に合わせる - nodes = joined_curve.nodes - - # MMD用補間曲線に変換 - joined_bz = scale_bezier(MVector2D(nodes[0, 0], nodes[1, 0]), MVector2D(nodes[0, 1], nodes[1, 1]), \ - MVector2D(nodes[0, 2], nodes[1, 2]), MVector2D(nodes[0, 3], nodes[1, 3])) - logger.debug("f: %s, %s, values: %s, nodes: %s, full_ys: %s, reduced_ys: %s, diff_ys: %s, diff_limit: %s, diff_large: %s, joined_bz: %s, %s, fit: %s", \ - fno, bone_name, values, joined_curve.nodes, full_ys, reduced_ys, diff_ys, diff_limit, np.count_nonzero(diff_large) > 0, joined_bz[1], joined_bz[2], \ - is_fit_bezier_mmd(joined_bz, offset)) - - if np.count_nonzero(diff_large) > 0: - # 差が大きい箇所がある場合、分割不可 - return None - - if not is_fit_bezier_mmd(joined_bz, offset): - # 補間曲線がMMD補間曲線内に収まらない場合、NG - return None - - # オフセット込みの場合、MMD用補間曲線枠内に収める - fit_bezier_mmd(joined_bz) - - # すべてクリアした場合、補間曲線採用 - return joined_bz - except Exception as e: - # エラーレベルは落として表に出さない - logger.debug("ベジェ曲線生成失敗", e) - return None - + return_tuple = c_join_value_2_bezier(fno, bone_name, values, offset, diff_limit) + return return_tuple[0], return_tuple[1] cdef tuple c_join_value_2_bezier(int fno, str bone_name, list values, double offset, double diff_limit): if len(values) <= 2: # 次数が1の場合、線形補間 logger.debug("次数1: values: %s", values) return (LINEAR_MMD_INTERPOLATION, []) - + cdef np.ndarray[np.double_t, ndim=1] xs, yx cdef np.ndarray[np.float_t, ndim=1] bz_x, bz_y, reduce_bz_x, reduce_bz_y, bezier_x, diff_ys, full_ys, reduced_ys, diff_large cdef np.ndarray[np.float_t, ndim=2] nodes @@ -286,7 +160,7 @@ cdef tuple c_join_value_2_bezier(int fno, str bone_name, list values, double off # カトマル曲線をベジェ曲線に変換する (bz_x, bz_y) = convert_catmullrom_2_bezier(np.concatenate([[None], xs, [None]]), np.concatenate([[None], ys, [None]])) - logger.debug("bz_x: %s, bz_y: %s", list(bz_x), list(bz_y)) + logger.debug("bz_x: %s, bz_y: %s", bz_x, bz_y) if len(bz_x) == 0: # 始点と終点が指定されていて、カトマル曲線が描けなかった場合、線形補間 @@ -315,14 +189,12 @@ cdef tuple c_join_value_2_bezier(int fno, str bone_name, list values, double off bz_x = full_curve.nodes[0] bz_y = full_curve.nodes[1] logger.test("START bz_x: %s, bz_y: %s", bz_x, bz_y) - - reduced_nodes = bezier.hazmat.curve_helpers.full_reduce(full_curve.nodes) # 3次になるまでベジェ曲線を繋いで減らしていく - while len(bz_x) > 5: + while len(bz_x) > 4: reduced_curve_list = [] - for n in range(0, degree + 1, 4): + for n in range(0, degree + 1, 5): reduce_bz_x = bz_x[n:n + 5] reduce_bz_y = bz_y[n:n + 5] logger.test("n: %s, reduce_bz_x: %s, reduce_bz_y: %s", n, reduce_bz_x, reduce_bz_y) @@ -341,20 +213,17 @@ cdef tuple c_join_value_2_bezier(int fno, str bone_name, list values, double off bz_y = np.empty(0) for reduced_curve in reduced_curve_list: - bz_x = np.append(bz_x, reduced_curve.nodes[0, -1]) - bz_y = np.append(bz_y, reduced_curve.nodes[1, -1]) + bz_x = np.append(bz_x, reduced_curve.nodes[0]) + bz_y = np.append(bz_y, reduced_curve.nodes[1]) logger.test("NEXT bz_x: %s, bz_y: %s", bz_x, bz_y) - - reduced_curve = bezier.Curve(np.asfortranarray([bz_x, bz_y]), degree=(len(bz_x) - 1)) - joined_curve = reduced_curve.reduce_() logger.test("FINISH bz_x: %s, bz_y: %s", bz_x, bz_y) # bz_x = [full_curve.nodes[0][0]] + list(bz_x) + [full_curve.nodes[0][-1]] # bz_y = [full_curve.nodes[0][0]] + list(bz_y) + [full_curve.nodes[0][-1]] - # joined_curve = bezier.Curve(np.asfortranarray([bz_x, bz_y]), degree=(len(bz_x) - 1)) + joined_curve = bezier.Curve(np.asfortranarray([bz_x, bz_y]), degree=(len(bz_x) - 1)) logger.test("joined_curve: %s", joined_curve.nodes) @@ -378,22 +247,9 @@ cdef tuple c_join_value_2_bezier(int fno, str bone_name, list values, double off # 差が一定未満である場合、ベジェ曲線をMMD補間曲線に合わせる nodes = joined_curve.nodes - # 標準化 - x_min = np.min(nodes[0]) - x_max = np.max(nodes[0]) - y_min = np.min(nodes[1]) - y_max = np.max(nodes[1]) - - x_diff = x_max - x_min - y_diff = y_max - y_min - - p1 = MVector2D((nodes[0, 0] - x_min) / x_diff, (nodes[1, 0] - y_min) / y_diff) - p2 = MVector2D((nodes[0, 1] - x_min) / x_diff, (nodes[1, 1] - y_min) / y_diff) - p3 = MVector2D((nodes[0, 2] - x_min) / x_diff, (nodes[1, 2] - y_min) / y_diff) - p4 = MVector2D((nodes[0, 3] - x_min) / x_diff, (nodes[1, 3] - y_min) / y_diff) - # MMD用補間曲線に変換 - joined_bz = scale_bezier(p1, p2, p3, p4) + joined_bz = scale_bezier(MVector2D(nodes[0, 0], nodes[1, 0]), MVector2D(nodes[0, 1], nodes[1, 1]), \ + MVector2D(nodes[0, 2], nodes[1, 2]), MVector2D(nodes[0, 3], nodes[1, 3])) logger.debug("f: %s, %s, values: %s, nodes: %s, full_ys: %s, reduced_ys: %s, diff_ys: %s, diff_limit: %s, diff_large: %s, joined_bz: %s, %s, fit: %s", \ fno, bone_name, values, joined_curve.nodes, full_ys, reduced_ys, diff_ys, diff_limit, np.count_nonzero(diff_large) > 0, joined_bz[1], joined_bz[2], \ is_fit_bezier_mmd(joined_bz, offset)) @@ -404,16 +260,18 @@ cdef tuple c_join_value_2_bezier(int fno, str bone_name, list values, double off if not is_fit_bezier_mmd(joined_bz, offset): # 補間曲線がMMD補間曲線内に収まらない場合、NG - f_prime = np.gradient(values) - sign = np.concatenate([[0], np.diff(np.sign(np.diff(f_prime))), [0]]) - np_indices = np.where(np.abs(sign) > 1)[0] - - logger.debug("%s: f: %s, values: %s", bone_name, fno, values) - logger.debug("%s: f: %s, f_prime: %s", bone_name, fno, f_prime) - logger.debug("%s: f: %s, sign: %s", bone_name, fno, sign) - logger.debug("%s: f: %s, np_indices: %s", bone_name, fno, np_indices) - return (None, np_indices) + # 差分の大きなところを返す + diff_large = np.where(np.abs(diff_ys) > (diff_limit * 0.5 * (offset + 1)), 1, 0).astype(np.float) + if np.count_nonzero(diff_large) > 0: + return (None, np.where(diff_large)[0].tolist()) + + # 差分の大きなところを返す + diff_large = np.where(np.abs(diff_ys) > 0, 1, 0).astype(np.float) + if np.count_nonzero(diff_large) > 0: + return (None, np.where(diff_large)[0].tolist()) + + return (None, []) # オフセット込みの場合、MMD用補間曲線枠内に収める fit_bezier_mmd(joined_bz) @@ -456,8 +314,8 @@ cdef tuple convert_catmullrom_2_bezier(np.ndarray xs, np.ndarray ys): continue if not p0 and p3: - # bz_x.append(p1.x()) - # bz_y.append(p1.y()) + bz_x.append(p1.x()) + bz_y.append(p1.y()) # p0が空の場合、始点 B = (p1 * (1 / 2)) - p2 + (p3 * (1 / 2)) @@ -486,8 +344,8 @@ cdef tuple convert_catmullrom_2_bezier(np.ndarray xs, np.ndarray ys): bz_y.append(s1.y()) bz_y.append(s2.y()) - # bz_x.append(xs[-2]) - # bz_y.append(ys[-2]) + bz_x.append(xs[-2]) + bz_y.append(ys[-2]) return (np.array(bz_x, dtype=np.float64), np.array(bz_y, dtype=np.float64)) @@ -707,4 +565,3 @@ cdef int round_integer(double t): # pythonは偶数丸めなので、整数部で丸めた後、元に戻す return round(round(t2, -6) / 1000000) - diff --git a/vmdising_np64.spec b/vmdising_np64.spec index 05fc06a..4bac54d 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,7 +26,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β67_64bit', + name='VmdSizing_5.01_β68_64bit', debug=False, bootloader_ignore_signals=False, strip=False, From 7763a6a6d8bd156e2486b323f3a9f8c1db3871f7 Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Sat, 21 Nov 2020 06:49:56 +0900 Subject: [PATCH 36/37] =?UTF-8?q?=E3=83=AA=E3=83=AA=E3=83=BC=E3=82=B9?= =?UTF-8?q?=E6=BA=96=E5=82=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- archive/Readme.txt | 68 ++++++++++++++++++++------------------ src/executor.py | 2 +- src/utils/MBezierUtils.pxd | 2 +- src/utils/MBezierUtils.pyx | 2 +- vmdising_np64.spec | 2 +- 5 files changed, 39 insertions(+), 37 deletions(-) diff --git a/archive/Readme.txt b/archive/Readme.txt index e8fb688..af3f76d 100644 --- a/archive/Readme.txt +++ b/archive/Readme.txt @@ -31,6 +31,9 @@ @VMDTCWOɕl[VΉƃX^Xlj␳Ă݂yver5.00z https://www.nicovideo.jp/watch/sm37143852 +@VMDTCWO𑬂EmESɂĂ݂yver5.01z +https://www.nicovideo.jp/watch/sm37848503 + @u}K https://ch.nicovideo.jp/miu200521358/blomaga/ar1919098 @@ -44,8 +47,9 @@ https://seiga.nicovideo.jp/seiga/im9755721 @EVMDSizing_5.01_64bit.exe@@@c@c[{́iver5.0064bitł݂̂łj @EReadme.txt@@@@@@@@@@c@h~ @EVMDTCWOWiki@@@@@@ c@Wikiւ̃N -@E[tuTv@@@@@@c@[tũTvWiڍׂ͒QƁj +@ERecc[pÉ@@@@c@Recc[pÉւ̃N @EꊇTCWOTv.csv@@c@ꊇTCWÕTvCSV +@E[tuTv@@@@@@c@[tũTvWiڍׂ͒QƁj ---------------------------------------------------------------- @‹ @@ -63,7 +67,7 @@ https://seiga.nicovideo.jp/seiga/im9755721 EnCXybNł́Aŕ񏈗ŝŁAʏł1.3{炢s܂B @Aׂ̕傫Ȃ̂ŁAPC̐\vĕ̕悳łB -EOo͔ł́Av4̍̂悤ɁAoVMDt@CpXƓƂɃOt@Co͂܂B +EOł́Av4̍̂悤ɁAoVMDt@CpXƓƂɃOt@Co͂܂B ---------------------------------------------------------------- @@ -209,6 +213,8 @@ A ̓IȒĺuꊇTCWOTv.csvvmFB wb_̈sڂ͓ǂݔ΂܂B +f[^Ɂu,(J})vgȂʼn +o̓f[^́AGUIftHgƓAVMDt@CƓꏊɏo͂܂B LibreOfficeŊJۂ̒ӓ_ @EeLXg̃C|[g_CAOŁA؂̃IvVuZ~RvOĂ @@ -221,58 +227,54 @@ A ut@Cvuv^úu[V쐬fPMXvt@CpXB 04 c [Vϊ惂fPMX ut@Cvuv^úu[Vϊ惂fPMXvt@CpXB -05 c oVMD - ut@Cvuv^úuoVMDvB󗓂̏ꍇAΏۃ[VVMD/VPDƓKwɏo͂܂B -06 c Z^[XZ␳ +05 c Z^[XZ␳ ut@Cvuv^úuX^Xlj␳Z^[XZ␳v`FbN{bNXB0:A1:L̂ꂩK{łB -07 c 㔼g␳ +06 c 㔼g␳ ut@Cvuv^úuX^Xlj␳㔼g␳v`FbN{bNXB0:A1:L̂ꂩK{łB -08 c g␳ +07 c g␳ ut@Cvuv^úuX^Xlj␳g␳v`FbN{bNXB0:A1:L̂ꂩK{łB -09 c hj␳ +08 c hj␳ ut@Cvuv^úuX^Xlj␳hj␳v`FbN{bNXB0:A1:L̂ꂩK{łB -10 c ‚ܐ␳ +09 c ‚ܐ␳ ut@Cvuv^úuX^Xlj␳‚ܐ␳v`FbN{bNXB0:A1:L̂ꂩK{łB -11 c ‚ܐhj␳ +10 c ‚ܐhj␳ ut@Cvuv^úuX^Xlj␳‚ܐhj␳v`FbN{bNXB0:A1:L̂ꂩK{łB -12 c ␳ +11 c ␳ ut@Cvuv^úuX^Xlj␳␳v`FbN{bNXB0:A1:L̂ꂩK{łB -13 c Z^[Y␳ +12 c Z^[Y␳ ut@Cvuv^úuX^Xlj␳Z^[Y␳v`FbN{bNXB0:A1:L̂ꂩK{łB -14 c 蕪U +13 c 蕪U ut@Cvuv^úu蕪Uv`FbN{bNXB0:A1:L̂ꂩK{łB -15 c [tu +14 c [tu u[tv^úu[tuṽZbgBuu[t:u惂[t:傫;v1‚̃ZbgƂĂB i[tɃZ~RĂƎg܂BŌ̃Z~R͕K{łj -16 c ڐG +15 c ڐG urv^úuڐGv`FbN{bNXB0:A1:L̂ꂩK{łB -17 c ڐG +16 c ڐG urv^úuڐGv̑Ώۍ̖XgBu̖;v̂悤ɔpZ~RŌqłB i̖ɃZ~RĂƎg܂BŌ̃Z~R͕K{łj -18 c ʒu킹 +17 c ʒu킹 urv^úuʒu킹v`FbN{bNXB0:A1:L̂ꂩK{łB -19 c wʒu킹 +18 c wʒu킹 urv^úuẅʒuňʒu킹sv`FbN{bNXB0:A1:L̂ꂩK{łB l[V̏ꍇAI0ɂȂ܂B -20 c ʒu킹 +19 c ʒu킹 urv^úuƂ̈ʒu킹ꏏɍsv`FbN{bNXB0:A1:L̂ꂩK{łB -21 c ̋ - urv^úuԂ̋vXC_[B0ȏ̒lK{łB -22 c w̋ - urv^úuwԂ̋vXC_[B0ȏ̒lK{łB -23 c Ƃ̋ - urv^úuƏƂ̋vXC_[B0ȏ̒lK{łB -24 c r`FbNXLbv +20 c ̋ + urv^úuԂ̋vXC_[B0ȏ̒lK{łBGUIftHgĺA1l̏ꍇu1.7vAl̏ꍇu2.5vłB +21 c w̋ + urv^úuwԂ̋vXC_[B0ȏ̒lK{łBGUIftHǵu1,4vłB +22 c Ƃ̋ + urv^úuƏƂ̋vXC_[B0ȏ̒lK{łBGUIftHǵu1,2vłB +23 c r`FbNXLbv urv^úur`̃TCWO”\`FbNXLbvv`FbN{bNXB0:A1:L̂ꂩK{łB -25 c J[VVMD +24 c J[VVMD uJv^úuJ[VVMDvt@CpXBO[vPʂ1sڂ̒l̂ݎQƂ܂B -26 c o̓JVMD - uJv^úuo̓JVMDvB󗓂̏ꍇAJ[VVMDƓKwɏo͂܂B -27 c “͈ +25 c “͈ uJv^úu“͈́vXC_[B1ȏ̒lK{łB -28 c J쐬fPMX +26 c J쐬fPMX uJv^úuJ쐬fPMXvt@CpXB -29 c SYItZbg +27 c SYItZbg uJv^úuSYItZbgvB @@ -394,7 +396,7 @@ https://github.com/miu200521358/vmd_sizing @ ---------------------------------------------------------------- -ver5.01@i2020/11/07j +ver5.01@i2020/11/21j @@E̍i30%Xs[hAbvj @@EiXe[^X\@\lj @@EꊇTCWO@\lj diff --git a/src/executor.py b/src/executor.py index 7a40b59..b02e215 100644 --- a/src/executor.py +++ b/src/executor.py @@ -17,7 +17,7 @@ from service.SizingService import SizingService from utils.MException import SizingException -VERSION_NAME = "ver5.01_β68" +VERSION_NAME = "ver5.01" # 指数表記なし、有効小数点桁数6、30を超えると省略あり、一行の文字数200 np.set_printoptions(suppress=True, precision=6, threshold=30, linewidth=200) diff --git a/src/utils/MBezierUtils.pxd b/src/utils/MBezierUtils.pxd index 9c163da..a79791d 100644 --- a/src/utils/MBezierUtils.pxd +++ b/src/utils/MBezierUtils.pxd @@ -7,7 +7,7 @@ from module.MMath cimport MRect, MVector2D, MVector3D, MVector4D, MQuaternion, M cdef double calc_catmull_rom_one_point(double x, double v0, double v1, double v2, double v3) except? -1 -cdef np.ndarray calc_value_from_catmullrom(str bone_name, list fnos, list values) +cpdef np.ndarray calc_value_from_catmullrom(str bone_name, list fnos, list values) cdef bint fit_bezier_mmd(list bzs) diff --git a/src/utils/MBezierUtils.pyx b/src/utils/MBezierUtils.pyx index 2e5d380..d2ec08a 100644 --- a/src/utils/MBezierUtils.pyx +++ b/src/utils/MBezierUtils.pyx @@ -77,7 +77,7 @@ cdef double calc_catmull_rom_one_point(double x, double v0, double v1, double v2 # 指定したすべての値をカトマル曲線として計算する -cdef np.ndarray calc_value_from_catmullrom(str bone_name, list fnos, list values): +cpdef np.ndarray calc_value_from_catmullrom(str bone_name, list fnos, list values): cdef np.ndarray[np.float_t, ndim=1] y_intpol cdef list prev_list, next_list cdef int fidx, sfno, efno, res diff --git a/vmdising_np64.spec b/vmdising_np64.spec index 4bac54d..1f48be8 100644 --- a/vmdising_np64.spec +++ b/vmdising_np64.spec @@ -26,7 +26,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='VmdSizing_5.01_β68_64bit', + name='VmdSizing_5.01_64bit', debug=False, bootloader_ignore_signals=False, strip=False, From bd2213e5974043b2c1d38f78126cef2cae76da7d Mon Sep 17 00:00:00 2001 From: miu200521358 Date: Sat, 21 Nov 2020 10:02:48 +0900 Subject: [PATCH 37/37] Update Readme.txt --- archive/Readme.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/archive/Readme.txt b/archive/Readme.txt index af3f76d..a212578 100644 --- a/archive/Readme.txt +++ b/archive/Readme.txt @@ -402,6 +402,7 @@ ver5.01 @@EꊇTCWO@\lj @@E蕪U̐x @@EJTCWOɁA“͈͐IvVlj +@@EX[WOE[tuhEԋȐr[[A[VT|[^[ɈڐA @@EBugfix: JTCWOŎXikڔ䗦jȂĂ̂C @@EBugfix: vpdʒu킹ƎsĂ̂C @@EBugfix: ڐGœ{[ɃEFCgĂ钸_‚Ȃ