Upload 646 files
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .gitattributes +27 -0
- vae/com3d/2018-04-18_225822/crash.dmp +0 -0
- vae/com3d/2018-04-18_225822/error.log +25 -0
- vae/com3d/2018-04-18_225822/output_log.txt +0 -0
- vae/com3d/2018-04-18_225822/report.ini +0 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/All Purpose arc Editor (Sybaris Arc Edtor(English))/Readme.txt +17 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/__init__.py +310 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/anm_export.py +334 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/anm_import.py +213 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/append_data.blend +3 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/common.py +635 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/console_toggle.py +3 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/fileutil.py +83 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/kiss.png +0 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/mate_export.py +257 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/mate_import.py +224 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_DATA_PT_context_arm.py +300 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_DATA_PT_modifiers.py +217 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_DATA_PT_vertex_groups.py +127 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_IMAGE_HT_header.py +14 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_IMAGE_PT_image_properties.py +12 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_INFO_HT_header.py +48 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_INFO_MT_add.py +56 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_INFO_MT_curve_add.py +186 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_INFO_MT_help.py +154 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_MATERIAL_PT_context_material.py +822 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_MESH_MT_shape_key_specials.py +575 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_MESH_MT_vertex_group_specials.py +672 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_OBJECT_PT_context_object.py +184 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_OBJECT_PT_transform.py +42 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_RENDER_PT_bake.py +1680 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_RENDER_PT_render.py +306 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_TEXTURE_PT_context_texture.py +568 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_TEXT_HT_header.py +174 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_VIEW3D_MT_edit_mesh_specials.py +56 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_VIEW3D_MT_pose_apply.py +113 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_VIEW3D_PT_tools_mesh_shapekey.py +16 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_VIEW3D_PT_tools_weightpaint.py +394 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/model_export.py +849 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/model_import.py +671 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/tex_export.py +115 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/tex_import.py +71 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Mod Search (English)/CM3D2 Mod Search.exe +0 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Mod Search (English)/CM3D2 Mod Search.html +22 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Mod Search (English)/CM3D2 Mod Search.ini +9 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Mod Search (English)/Readme & Modder List.txt +213 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/MenuEdit2017420/Dictionary and How to.txt +102 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/MenuEdit2017420/[CM3D2]menuEdit.exe +0 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/MenuEdit2017420/readme.txt +4 -0
- vae/com3d/[CM3D2]English Mod Tools Pack/MenuEdit2017420/src/[CM3D2]menuEdit.ahk +274 -0
.gitattributes
CHANGED
@@ -80,3 +80,30 @@ vae/com3d/COM3D2x64_Data/resources.assets filter=lfs diff=lfs merge=lfs -text
|
|
80 |
vae/com3d/COM3D2x64_Data/resources.assets.resS filter=lfs diff=lfs merge=lfs -text
|
81 |
vae/com3d/COM3D2x64_Data/sharedassets0.assets filter=lfs diff=lfs merge=lfs -text
|
82 |
vae/com3d/COM3D2x64_Data/sharedassets0.assets.resS filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
80 |
vae/com3d/COM3D2x64_Data/resources.assets.resS filter=lfs diff=lfs merge=lfs -text
|
81 |
vae/com3d/COM3D2x64_Data/sharedassets0.assets filter=lfs diff=lfs merge=lfs -text
|
82 |
vae/com3d/COM3D2x64_Data/sharedassets0.assets.resS filter=lfs diff=lfs merge=lfs -text
|
83 |
+
vae/com3d/_AssetsBundleExtractor_2.1c/32bit/fmod.dll filter=lfs diff=lfs merge=lfs -text
|
84 |
+
vae/com3d/_AssetsBundleExtractor_2.1c/32bit/PVRTexLib.dll filter=lfs diff=lfs merge=lfs -text
|
85 |
+
vae/com3d/_AssetsBundleExtractor_2.1c/64bit/fmod64.dll filter=lfs diff=lfs merge=lfs -text
|
86 |
+
vae/com3d/_AssetsBundleExtractor_2.1c/64bit/PVRTexLib.dll filter=lfs diff=lfs merge=lfs -text
|
87 |
+
vae/com3d/_CM3D2_Toolkit_PR2/Tスタンス素体.blend filter=lfs diff=lfs merge=lfs -text
|
88 |
+
vae/com3d/_setup/CM3D2_ED_x64.exe filter=lfs diff=lfs merge=lfs -text
|
89 |
+
vae/com3d/_setup/CM3D2_EF.exe filter=lfs diff=lfs merge=lfs -text
|
90 |
+
vae/com3d/_setup/CM3D2.exe filter=lfs diff=lfs merge=lfs -text
|
91 |
+
vae/com3d/_setup/CM3D2x64.exe filter=lfs diff=lfs merge=lfs -text
|
92 |
+
vae/com3d/_setup/Sybaris/Plugins/UnityInjector/Config/EditMenuUtility.xml filter=lfs diff=lfs merge=lfs -text
|
93 |
+
vae/com3d/_setup/Sybaris/Plugins/UnityInjector/Config/PropMyItem.xml filter=lfs diff=lfs merge=lfs -text
|
94 |
+
vae/com3d/_setup/SybarisArcEditor.exe filter=lfs diff=lfs merge=lfs -text
|
95 |
+
vae/com3d/_Translation/Assets/_kor/sharedassets0/1367BD762E26A742.png filter=lfs diff=lfs merge=lfs -text
|
96 |
+
vae/com3d/_Translation/Assets/_kor/sharedassets0/[email protected] filter=lfs diff=lfs merge=lfs -text
|
97 |
+
vae/com3d/_Translation/Assets/_kor/sharedassets0/4209AC888C3D23D8.png filter=lfs diff=lfs merge=lfs -text
|
98 |
+
vae/com3d/_Translation/Textures/_ENG/Tutorial/cm3d2_tutorial_deskcustom.tex filter=lfs diff=lfs merge=lfs -text
|
99 |
+
vae/com3d/_Translation/Textures/_ENG/Tutorial/cm3d2_tutorial_skillselect00.tex filter=lfs diff=lfs merge=lfs -text
|
100 |
+
vae/com3d/_Translation/Textures/_kor/UI_Tutorial/cm3d2_tutorial_deskcustom.png filter=lfs diff=lfs merge=lfs -text
|
101 |
+
vae/com3d/_Translation/Textures/_kor/UI_Tutorial/cm3d2_tutorial_skillselect00.png filter=lfs diff=lfs merge=lfs -text
|
102 |
+
vae/com3d/_vmd/magnet/magミク修正版3.vmd filter=lfs diff=lfs merge=lfs -text
|
103 |
+
vae/com3d/_vmd/magnet/magミク別バージョン.vmd filter=lfs diff=lfs merge=lfs -text
|
104 |
+
vae/com3d/_vmd/magnet/magルカ修正版3.vmd filter=lfs diff=lfs merge=lfs -text
|
105 |
+
vae/com3d/_vmd/magnet/magルカ別バージョン.vmd filter=lfs diff=lfs merge=lfs -text
|
106 |
+
vae/com3d/_vmd/いーあるモーションせっと/いーあるぐみさん.vmd filter=lfs diff=lfs merge=lfs -text
|
107 |
+
vae/com3d/_vmd/いーあるモーションせっと/いーあるりんちゃんさん.vmd filter=lfs diff=lfs merge=lfs -text
|
108 |
+
vae/com3d/\[CM3D2\]English[[:space:]]Mod[[:space:]]Tools[[:space:]]Pack/\[CM3D2\]Various[[:space:]]Modding[[:space:]]Tools[[:space:]]151220[[:space:]](English)/T[[:space:]]Pose[[:space:]]body.blend filter=lfs diff=lfs merge=lfs -text
|
109 |
+
vae/com3d/\[CM3D2\]English[[:space:]]Mod[[:space:]]Tools[[:space:]]Pack/CM3D2[[:space:]]Converter[[:space:]](English)/append_data.blend filter=lfs diff=lfs merge=lfs -text
|
vae/com3d/2018-04-18_225822/crash.dmp
ADDED
Binary file (146 kB). View file
|
|
vae/com3d/2018-04-18_225822/error.log
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Unity Player [version: Unity 4.7.1f1_68064779abd0]
|
2 |
+
|
3 |
+
CM3D2x64.exe caused an Access Violation (0xc0000005)
|
4 |
+
in module CM3D2x64.exe at 0033:d76fa030.
|
5 |
+
|
6 |
+
Error occurred at 2018-04-18_225830.
|
7 |
+
D:\CM3D2\CM3D2x64.exe, run by TOMOKI.
|
8 |
+
24% memory in use.
|
9 |
+
32716 MB physical memory [24700 MB free].
|
10 |
+
37580 MB paging file [28009 MB free].
|
11 |
+
134217728 MB user address space [134216871 MB free].
|
12 |
+
Write to location d76fa030 caused an access violation.
|
13 |
+
|
14 |
+
Context:
|
15 |
+
RDI: 0x0670e530 RSI: 0x152a4e30 RAX: 0xd76fa030
|
16 |
+
RBX: 0x152a4e10 RCX: 0x152a4e30 RDX: 0x152a4e30
|
17 |
+
RIP: 0xd76fa030 RBP: 0x068ef530 SegCs: 0x00000033
|
18 |
+
EFlags: 0x00010206 RSP: 0x068ef498 SegSs: 0x0000002b
|
19 |
+
R8: 0x068ef6c0 R9: 0x00000000 R10: 0x00000000
|
20 |
+
R11: 0x068ef4c8 R12: 0x06064d48 R13: 0x152a4e10
|
21 |
+
R14: 0x06064d48 R15: 0x00000000
|
22 |
+
|
23 |
+
Bytes at CS:EIP:
|
24 |
+
?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ??
|
25 |
+
== [end of error.log] ==
|
vae/com3d/2018-04-18_225822/output_log.txt
ADDED
The diff for this file is too large to render.
See raw diff
|
|
vae/com3d/2018-04-18_225822/report.ini
ADDED
Binary file (630 Bytes). View file
|
|
vae/com3d/[CM3D2]English Mod Tools Pack/All Purpose arc Editor (Sybaris Arc Edtor(English))/Readme.txt
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
This is the best application for anything having to do with arc files by far.
|
2 |
+
A very simple and handy graphical interface makes locating the files you want to extract/replace etc very easy and now this app is 99% in english.
|
3 |
+
|
4 |
+
***To make use of it simply drop it in your install directory (your CM3D2 folder) or wherever your main CM3D2 application is located. Then just run it and you are good to go.
|
5 |
+
Sybaris Arc editor is completely stand alone and does not need sybaris to work and can work with reipatcher.
|
6 |
+
|
7 |
+
This app will open every arc file it can find in one window so you can easily use the search function to search through every arc instantly.
|
8 |
+
It can even let you find specific mods as it allows you to view loaded mods too by menu icon and category all in the menu tab. and it has many more features and tools.
|
9 |
+
|
10 |
+
NOTE: The app can take some time to show on screen after it has been opened because opening every single arc in the game isn't exactly easy.
|
11 |
+
But the wait is minimal and worth it compared to the benefits.
|
12 |
+
|
13 |
+
NOTE 2: With this newer version of SAE the load times are cut down by a lot and at most you will have a short wait time while switching tabs.
|
14 |
+
|
15 |
+
I take no credit for creating this application. I only take credit for translating and extend credit to Exkirion on hongfire for also translating.
|
16 |
+
|
17 |
+
Don't abuse your miedos!
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/__init__.py
ADDED
@@ -0,0 +1,310 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# アドオンを読み込む時に最初にこのファイルが読み込まれます
|
2 |
+
|
3 |
+
# アドオン情報
|
4 |
+
bl_info = {
|
5 |
+
"name" : "CM3D2 Converter",
|
6 |
+
"author" : "@saidenka_cm3d2",
|
7 |
+
"version" : (2017, 5, 16, 21, 39, 32),
|
8 |
+
"blender" : (2, 78, 0),
|
9 |
+
"location" : "File > Import/Export > CM3D2 Model (.model)",
|
10 |
+
"description" : "A plugin dedicated to the editing, importing, and exporting of CM3D2 .Model Files.",
|
11 |
+
"warning" : "",
|
12 |
+
"wiki_url" : "https://github.com/CM3Duser/Blender-CM3D2-Converter",
|
13 |
+
"tracker_url" : "https://twitter.com/saidenka_cm3d2",
|
14 |
+
"category" : "Import-Export"
|
15 |
+
}
|
16 |
+
|
17 |
+
# サブスクリプト群をインポート
|
18 |
+
if "bpy" in locals():
|
19 |
+
import imp
|
20 |
+
|
21 |
+
imp.reload(common)
|
22 |
+
|
23 |
+
imp.reload(model_import)
|
24 |
+
imp.reload(model_export)
|
25 |
+
|
26 |
+
imp.reload(anm_import)
|
27 |
+
imp.reload(anm_export)
|
28 |
+
|
29 |
+
imp.reload(tex_import)
|
30 |
+
imp.reload(tex_export)
|
31 |
+
|
32 |
+
imp.reload(mate_import)
|
33 |
+
imp.reload(mate_export)
|
34 |
+
|
35 |
+
imp.reload(misc_DATA_PT_context_arm)
|
36 |
+
imp.reload(misc_DATA_PT_modifiers)
|
37 |
+
imp.reload(misc_DATA_PT_vertex_groups)
|
38 |
+
imp.reload(misc_IMAGE_HT_header)
|
39 |
+
imp.reload(misc_IMAGE_PT_image_properties)
|
40 |
+
imp.reload(misc_INFO_HT_header)
|
41 |
+
imp.reload(misc_INFO_MT_add)
|
42 |
+
imp.reload(misc_INFO_MT_curve_add)
|
43 |
+
imp.reload(misc_INFO_MT_help)
|
44 |
+
imp.reload(misc_MATERIAL_PT_context_material)
|
45 |
+
imp.reload(misc_MESH_MT_shape_key_specials)
|
46 |
+
imp.reload(misc_MESH_MT_vertex_group_specials)
|
47 |
+
imp.reload(misc_OBJECT_PT_context_object)
|
48 |
+
imp.reload(misc_OBJECT_PT_transform)
|
49 |
+
imp.reload(misc_RENDER_PT_bake)
|
50 |
+
imp.reload(misc_RENDER_PT_render)
|
51 |
+
imp.reload(misc_TEXTURE_PT_context_texture)
|
52 |
+
imp.reload(misc_TEXT_HT_header)
|
53 |
+
imp.reload(misc_VIEW3D_MT_edit_mesh_specials)
|
54 |
+
imp.reload(misc_VIEW3D_MT_pose_apply)
|
55 |
+
imp.reload(misc_VIEW3D_PT_tools_weightpaint)
|
56 |
+
imp.reload(misc_VIEW3D_PT_tools_mesh_shapekey)
|
57 |
+
|
58 |
+
else:
|
59 |
+
from . import common
|
60 |
+
|
61 |
+
from . import model_import
|
62 |
+
from . import model_export
|
63 |
+
|
64 |
+
from . import anm_import
|
65 |
+
from . import anm_export
|
66 |
+
|
67 |
+
from . import tex_import
|
68 |
+
from . import tex_export
|
69 |
+
|
70 |
+
from . import mate_import
|
71 |
+
from . import mate_export
|
72 |
+
|
73 |
+
from . import misc_DATA_PT_context_arm
|
74 |
+
from . import misc_DATA_PT_modifiers
|
75 |
+
from . import misc_DATA_PT_vertex_groups
|
76 |
+
from . import misc_IMAGE_HT_header
|
77 |
+
from . import misc_IMAGE_PT_image_properties
|
78 |
+
from . import misc_INFO_HT_header
|
79 |
+
from . import misc_INFO_MT_add
|
80 |
+
from . import misc_INFO_MT_curve_add
|
81 |
+
from . import misc_INFO_MT_help
|
82 |
+
from . import misc_MATERIAL_PT_context_material
|
83 |
+
from . import misc_MESH_MT_shape_key_specials
|
84 |
+
from . import misc_MESH_MT_vertex_group_specials
|
85 |
+
from . import misc_OBJECT_PT_context_object
|
86 |
+
from . import misc_OBJECT_PT_transform
|
87 |
+
from . import misc_RENDER_PT_bake
|
88 |
+
from . import misc_RENDER_PT_render
|
89 |
+
from . import misc_TEXTURE_PT_context_texture
|
90 |
+
from . import misc_TEXT_HT_header
|
91 |
+
from . import misc_VIEW3D_MT_edit_mesh_specials
|
92 |
+
from . import misc_VIEW3D_MT_pose_apply
|
93 |
+
from . import misc_VIEW3D_PT_tools_weightpaint
|
94 |
+
from . import misc_VIEW3D_PT_tools_mesh_shapekey
|
95 |
+
|
96 |
+
import bpy, os.path, bpy.utils.previews
|
97 |
+
|
98 |
+
# アドオン設定
|
99 |
+
class AddonPreferences(bpy.types.AddonPreferences):
|
100 |
+
bl_idname = __name__
|
101 |
+
|
102 |
+
cm3d2_path = bpy.props.StringProperty(name="CM3D2 Location", subtype='DIR_PATH', description="You should set the correct directory if you used a different one.")
|
103 |
+
backup_ext = bpy.props.StringProperty(name="Backup Extension (Must not be left blank)", description="The previous Model file with the same name will be given an extension.", default='bak')
|
104 |
+
|
105 |
+
scale = bpy.props.FloatProperty(name="Scale", description="The scale at which the models are imported and exported", default=5, min=0.01, max=100, soft_min=0.01, soft_max=100, step=10, precision=2)
|
106 |
+
is_convert_bone_weight_names = bpy.props.BoolProperty(name="Convert weight names for Blender", default=False, description="Will change the options default when importing or exporting.")
|
107 |
+
model_default_path = bpy.props.StringProperty(name="Model Default Path", subtype='DIR_PATH', description="If set. The file selection will open here.")
|
108 |
+
model_import_path = bpy.props.StringProperty(name="Model Default Import Path", subtype='FILE_PATH', description="When importing a .model file. The file selection prompt will begin here.")
|
109 |
+
model_export_path = bpy.props.StringProperty(name="Model Default Export Path", subtype='FILE_PATH', description="When exporting a .model file. The file selection prompt will begin here.")
|
110 |
+
|
111 |
+
anm_default_path = bpy.props.StringProperty(name=".anm Default Path", subtype='DIR_PATH', description="If set. The file selection will open here.")
|
112 |
+
anm_import_path = bpy.props.StringProperty(name=".anm Default Import Path", subtype='FILE_PATH', description="When importing a .anm file. The file selection prompt will begin here.")
|
113 |
+
anm_export_path = bpy.props.StringProperty(name=".anm Default Export Path", subtype='FILE_PATH', description="When exporting a .anm file. The file selection prompt will begin here.")
|
114 |
+
|
115 |
+
tex_default_path = bpy.props.StringProperty(name=".tex Default Path", subtype='DIR_PATH', description="If set. The file selection will open here.")
|
116 |
+
tex_import_path = bpy.props.StringProperty(name=".tex Default Import Path", subtype='FILE_PATH', description="When importing a .tex file. The file selection prompt will begin here.")
|
117 |
+
tex_export_path = bpy.props.StringProperty(name=".tex Default Export Path", subtype='FILE_PATH', description="When exporting a .tex file. The file selection prompt will begin here.")
|
118 |
+
|
119 |
+
mate_default_path = bpy.props.StringProperty(name=".mate Default Path", subtype='DIR_PATH', description="If set. The file selection will open here.")
|
120 |
+
mate_unread_same_value = bpy.props.BoolProperty(name="Delete if there are two or more same values", default=True, description="_ShadowColor")
|
121 |
+
mate_import_path = bpy.props.StringProperty(name=".mate Default Import Path", subtype='FILE_PATH', description="When importing a .mate file. The file selection prompt will begin here.")
|
122 |
+
mate_export_path = bpy.props.StringProperty(name=".mate Default Export Path", subtype='FILE_PATH', description="When exporting a .mate file. The file selection prompt will begin here.")
|
123 |
+
|
124 |
+
is_replace_cm3d2_tex = bpy.props.BoolProperty(name="Search for Tex File", default=True, description="Sets the default of the option to search for tex files")
|
125 |
+
default_tex_path0 = bpy.props.StringProperty(name="Tex file search area", subtype='DIR_PATH', description="Search here for tex files")
|
126 |
+
default_tex_path1 = bpy.props.StringProperty(name="Tex file search area", subtype='DIR_PATH', description="Search here for tex files")
|
127 |
+
default_tex_path2 = bpy.props.StringProperty(name="Tex file search area", subtype='DIR_PATH', description="Search here for tex files")
|
128 |
+
default_tex_path3 = bpy.props.StringProperty(name="Tex file search area", subtype='DIR_PATH', description="Search here for tex files")
|
129 |
+
|
130 |
+
|
131 |
+
new_mate_tex_color = bpy.props.FloatVectorProperty(name="Material Color", default=(0, 0, 1, 1), min=0, max=1, soft_min=0, soft_max=1, step=10, precision=2, subtype='COLOR', size=4)
|
132 |
+
|
133 |
+
new_mate_toonramp_name = bpy.props.StringProperty(name="_ToonRamp Name", default="toonGrayA1")
|
134 |
+
new_mate_toonramp_path = bpy.props.StringProperty(name="_ToonRamp Path", default=r"Assets\texture\texture\toon\toonGrayA1.png")
|
135 |
+
|
136 |
+
new_mate_shadowratetoon_name = bpy.props.StringProperty(name="_ShadowRateToon Name", default="toonDress_shadow")
|
137 |
+
new_mate_shadowratetoon_path = bpy.props.StringProperty(name="_ShadowRateToon Path", default=r"Assets\texture\texture\toon\toonDress_shadow.png")
|
138 |
+
|
139 |
+
new_mate_color = bpy.props.FloatVectorProperty(name="_Color", default=(1, 1, 1, 1), min=0, max=1, soft_min=0, soft_max=1, step=10, precision=2, subtype='COLOR', size=4)
|
140 |
+
new_mate_shadowcolor = bpy.props.FloatVectorProperty(name="_ShadowColor", default=(0, 0, 0, 1), min=0, max=1, soft_min=0, soft_max=1, step=10, precision=2, subtype='COLOR', size=4)
|
141 |
+
new_mate_rimcolor = bpy.props.FloatVectorProperty(name="_RimColor", default=(0.5, 0.5, 0.5, 1), min=0, max=1, soft_min=0, soft_max=1, step=10, precision=2, subtype='COLOR', size=4)
|
142 |
+
new_mate_outlinecolor = bpy.props.FloatVectorProperty(name="_OutlineColor", default=(0, 0, 0, 1), min=0, max=1, soft_min=0, soft_max=1, step=10, precision=2, subtype='COLOR', size=4)
|
143 |
+
|
144 |
+
new_mate_shininess = bpy.props.FloatProperty(name="_Shininess", default=0, min=-100, max=100, soft_min=-100, soft_max=100, step=1, precision=2)
|
145 |
+
new_mate_outlinewidth = bpy.props.FloatProperty(name="_OutlineWidth", default=0.0015, min=-100, max=100, soft_min=-100, soft_max=100, step=1, precision=2)
|
146 |
+
new_mate_rimpower = bpy.props.FloatProperty(name="_RimPower", default=25, min=-100, max=100, soft_min=-100, soft_max=100, step=1, precision=2)
|
147 |
+
new_mate_rimshift = bpy.props.FloatProperty(name="_RimShift", default=0, min=-100, max=100, soft_min=-100, soft_max=100, step=1, precision=2)
|
148 |
+
new_mate_hirate = bpy.props.FloatProperty(name="_HiRate", default=0.5, min=-100, max=100, soft_min=-100, soft_max=100, step=1, precision=2)
|
149 |
+
new_mate_hipow = bpy.props.FloatProperty(name="_HiPow", default=0.001, min=-100, max=100, soft_min=-100, soft_max=100, step=1, precision=2)
|
150 |
+
|
151 |
+
def draw(self, context):
|
152 |
+
self.layout.label(text="These settings are not saved until you press the (Save User Settings) button", icon='QUESTION')
|
153 |
+
self.layout.prop(self, 'cm3d2_path', icon_value=common.preview_collections['main']['KISS'].icon_id)
|
154 |
+
self.layout.prop(self, 'backup_ext', icon='FILE_BACKUP')
|
155 |
+
|
156 |
+
box = self.layout.box()
|
157 |
+
box.label(text=".Model File", icon='MESH_ICOSPHERE')
|
158 |
+
row = box.row()
|
159 |
+
row.prop(self, 'scale', icon='MAN_SCALE')
|
160 |
+
row.prop(self, 'is_convert_bone_weight_names', icon='BLENDER')
|
161 |
+
box.prop(self, 'model_default_path', icon='FILESEL', text="Initial folder when selecting files")
|
162 |
+
|
163 |
+
box = self.layout.box()
|
164 |
+
box.label(text=".anm File", icon='POSE_HLT')
|
165 |
+
box.prop(self, 'anm_default_path', icon='FILESEL', text="Initial folder when selecting files")
|
166 |
+
|
167 |
+
box = self.layout.box()
|
168 |
+
box.label(text=".tex File", icon='FILE_IMAGE')
|
169 |
+
box.prop(self, 'tex_default_path', icon='FILESEL', text="Initial folder when selecting files")
|
170 |
+
|
171 |
+
box = self.layout.box()
|
172 |
+
box.label(text=".mate File", icon='MATERIAL')
|
173 |
+
box.prop(self, 'mate_unread_same_value', icon='DISCLOSURE_TRI_DOWN')
|
174 |
+
box.prop(self, 'mate_default_path', icon='FILESEL', text="Initial folder when selecting files")
|
175 |
+
|
176 |
+
box = self.layout.box()
|
177 |
+
box.label(text="Search for Tex File", icon='BORDERMOVE')
|
178 |
+
box.prop(self, 'is_replace_cm3d2_tex', icon='VIEWZOOM')
|
179 |
+
box.prop(self, 'default_tex_path0', icon='LAYER_ACTIVE', text="Part 1")
|
180 |
+
box.prop(self, 'default_tex_path1', icon='LAYER_ACTIVE', text="Part 2")
|
181 |
+
box.prop(self, 'default_tex_path2', icon='LAYER_ACTIVE', text="Part 3")
|
182 |
+
box.prop(self, 'default_tex_path3', icon='LAYER_ACTIVE', text="Part 4")
|
183 |
+
|
184 |
+
box = self.layout.box()
|
185 |
+
box.label(text="Defaults for when a new CM3d2 Material is Created.", icon='MATERIAL')
|
186 |
+
box.prop(self, 'new_mate_tex_color', icon='COLOR')
|
187 |
+
row = box.row()
|
188 |
+
row.prop(self, 'new_mate_toonramp_name', icon='BRUSH_TEXFILL')
|
189 |
+
row.prop(self, 'new_mate_toonramp_path', icon='ANIM')
|
190 |
+
row = box.row()
|
191 |
+
row.prop(self, 'new_mate_shadowratetoon_name', icon='BRUSH_TEXMASK')
|
192 |
+
row.prop(self, 'new_mate_shadowratetoon_path', icon='ANIM')
|
193 |
+
row = box.row()
|
194 |
+
row.prop(self, 'new_mate_color', icon='COLOR')
|
195 |
+
row.prop(self, 'new_mate_shadowcolor', icon='IMAGE_ALPHA')
|
196 |
+
row.prop(self, 'new_mate_rimcolor', icon='MATCAP_07')
|
197 |
+
row.prop(self, 'new_mate_outlinecolor', icon='SOLID')
|
198 |
+
row = box.row()
|
199 |
+
row.prop(self, 'new_mate_shininess', icon='MATCAP_05')
|
200 |
+
row.prop(self, 'new_mate_outlinewidth', icon='SOLID')
|
201 |
+
row.prop(self, 'new_mate_rimpower', icon='MATCAP_14')
|
202 |
+
row.prop(self, 'new_mate_rimshift', icon='ARROW_LEFTRIGHT')
|
203 |
+
row.prop(self, 'new_mate_hirate')
|
204 |
+
row.prop(self, 'new_mate_hipow')
|
205 |
+
|
206 |
+
row = self.layout.row()
|
207 |
+
row.operator('script.update_cm3d2_converter', icon='FILE_REFRESH')
|
208 |
+
row.menu('INFO_MT_help_CM3D2_Converter_RSS', icon='INFO')
|
209 |
+
|
210 |
+
# プラグインをインストールしたときの処理
|
211 |
+
def register():
|
212 |
+
bpy.utils.register_module(__name__)
|
213 |
+
|
214 |
+
bpy.types.INFO_MT_file_import.append(model_import.menu_func)
|
215 |
+
bpy.types.INFO_MT_file_export.append(model_export.menu_func)
|
216 |
+
|
217 |
+
bpy.types.INFO_MT_file_import.append(anm_import.menu_func)
|
218 |
+
bpy.types.INFO_MT_file_export.append(anm_export.menu_func)
|
219 |
+
|
220 |
+
bpy.types.IMAGE_MT_image.append(tex_import.menu_func)
|
221 |
+
bpy.types.IMAGE_MT_image.append(tex_export.menu_func)
|
222 |
+
|
223 |
+
bpy.types.TEXT_MT_text.append(mate_import.TEXT_MT_text)
|
224 |
+
bpy.types.TEXT_MT_text.append(mate_export.TEXT_MT_text)
|
225 |
+
|
226 |
+
bpy.types.DATA_PT_context_arm.append(misc_DATA_PT_context_arm.menu_func)
|
227 |
+
bpy.types.DATA_PT_modifiers.append(misc_DATA_PT_modifiers.menu_func)
|
228 |
+
bpy.types.DATA_PT_vertex_groups.append(misc_DATA_PT_vertex_groups.menu_func)
|
229 |
+
bpy.types.IMAGE_HT_header.append(misc_IMAGE_HT_header.menu_func)
|
230 |
+
bpy.types.IMAGE_PT_image_properties.append(misc_IMAGE_PT_image_properties.menu_func)
|
231 |
+
bpy.types.INFO_HT_header.append(misc_INFO_HT_header.menu_func)
|
232 |
+
bpy.types.INFO_MT_add.append(misc_INFO_MT_add.menu_func)
|
233 |
+
bpy.types.INFO_MT_curve_add.append(misc_INFO_MT_curve_add.menu_func)
|
234 |
+
bpy.types.INFO_MT_help.append(misc_INFO_MT_help.menu_func)
|
235 |
+
bpy.types.MATERIAL_PT_context_material.append(misc_MATERIAL_PT_context_material.menu_func)
|
236 |
+
bpy.types.MESH_MT_shape_key_specials.append(misc_MESH_MT_shape_key_specials.menu_func)
|
237 |
+
bpy.types.MESH_MT_vertex_group_specials.append(misc_MESH_MT_vertex_group_specials.menu_func)
|
238 |
+
bpy.types.OBJECT_PT_context_object.append(misc_OBJECT_PT_context_object.menu_func)
|
239 |
+
bpy.types.OBJECT_PT_transform.append(misc_OBJECT_PT_transform.menu_func)
|
240 |
+
bpy.types.RENDER_PT_bake.append(misc_RENDER_PT_bake.menu_func)
|
241 |
+
bpy.types.RENDER_PT_render.append(misc_RENDER_PT_render.menu_func)
|
242 |
+
bpy.types.TEXTURE_PT_context_texture.append(misc_TEXTURE_PT_context_texture.menu_func)
|
243 |
+
bpy.types.TEXT_HT_header.append(misc_TEXT_HT_header.menu_func)
|
244 |
+
bpy.types.VIEW3D_MT_edit_mesh_specials.append(misc_VIEW3D_MT_edit_mesh_specials.menu_func)
|
245 |
+
bpy.types.VIEW3D_MT_pose_apply.append(misc_VIEW3D_MT_pose_apply.menu_func)
|
246 |
+
bpy.types.VIEW3D_PT_tools_weightpaint.append(misc_VIEW3D_PT_tools_weightpaint.menu_func)
|
247 |
+
|
248 |
+
pcoll = bpy.utils.previews.new()
|
249 |
+
dir = os.path.dirname(__file__)
|
250 |
+
pcoll.load('KISS', os.path.join(dir, "kiss.png"), 'IMAGE')
|
251 |
+
common.preview_collections['main'] = pcoll
|
252 |
+
|
253 |
+
system = bpy.context.user_preferences.system
|
254 |
+
if not system.use_international_fonts:
|
255 |
+
system.use_international_fonts = True
|
256 |
+
if not system.use_translate_interface:
|
257 |
+
system.use_translate_interface = True
|
258 |
+
try:
|
259 |
+
import locale
|
260 |
+
if system.language == 'DEFAULT' and locale.getdefaultlocale()[0] != 'ja_JP':
|
261 |
+
system.language = 'en_US'
|
262 |
+
except: pass
|
263 |
+
|
264 |
+
# プラグインをアンインストールしたときの処理
|
265 |
+
def unregister():
|
266 |
+
bpy.utils.unregister_module(__name__)
|
267 |
+
|
268 |
+
bpy.types.INFO_MT_file_import.remove(model_import.menu_func)
|
269 |
+
bpy.types.INFO_MT_file_export.remove(model_export.menu_func)
|
270 |
+
|
271 |
+
bpy.types.INFO_MT_file_import.remove(anm_import.menu_func)
|
272 |
+
bpy.types.INFO_MT_file_export.remove(anm_export.menu_func)
|
273 |
+
|
274 |
+
bpy.types.IMAGE_MT_image.remove(tex_import.menu_func)
|
275 |
+
bpy.types.IMAGE_MT_image.remove(tex_export.menu_func)
|
276 |
+
|
277 |
+
bpy.types.TEXT_MT_text.remove(mate_import.TEXT_MT_text)
|
278 |
+
bpy.types.TEXT_MT_text.remove(mate_export.TEXT_MT_text)
|
279 |
+
|
280 |
+
bpy.types.DATA_PT_context_arm.remove(misc_DATA_PT_context_arm.menu_func)
|
281 |
+
bpy.types.DATA_PT_modifiers.remove(misc_DATA_PT_modifiers.menu_func)
|
282 |
+
bpy.types.DATA_PT_vertex_groups.remove(misc_DATA_PT_vertex_groups.menu_func)
|
283 |
+
bpy.types.IMAGE_HT_header.remove(misc_IMAGE_HT_header.menu_func)
|
284 |
+
bpy.types.IMAGE_PT_image_properties.remove(misc_IMAGE_PT_image_properties.menu_func)
|
285 |
+
bpy.types.INFO_HT_header.remove(misc_INFO_HT_header.menu_func)
|
286 |
+
bpy.types.INFO_MT_add.remove(misc_INFO_MT_add.menu_func)
|
287 |
+
bpy.types.INFO_MT_curve_add.remove(misc_INFO_MT_curve_add.menu_func)
|
288 |
+
bpy.types.INFO_MT_help.remove(misc_INFO_MT_help.menu_func)
|
289 |
+
bpy.types.MATERIAL_PT_context_material.remove(misc_MATERIAL_PT_context_material.menu_func)
|
290 |
+
bpy.types.MESH_MT_shape_key_specials.remove(misc_MESH_MT_shape_key_specials.menu_func)
|
291 |
+
bpy.types.MESH_MT_vertex_group_specials.remove(misc_MESH_MT_vertex_group_specials.menu_func)
|
292 |
+
bpy.types.OBJECT_PT_context_object.remove(misc_OBJECT_PT_context_object.menu_func)
|
293 |
+
bpy.types.OBJECT_PT_transform.remove(misc_OBJECT_PT_transform.menu_func)
|
294 |
+
bpy.types.RENDER_PT_bake.remove(misc_RENDER_PT_bake.menu_func)
|
295 |
+
bpy.types.RENDER_PT_render.remove(misc_RENDER_PT_render.menu_func)
|
296 |
+
bpy.types.TEXTURE_PT_context_texture.remove(misc_TEXTURE_PT_context_texture.menu_func)
|
297 |
+
bpy.types.TEXT_HT_header.remove(misc_TEXT_HT_header.menu_func)
|
298 |
+
bpy.types.VIEW3D_MT_edit_mesh_specials.remove(misc_VIEW3D_MT_edit_mesh_specials.menu_func)
|
299 |
+
bpy.types.VIEW3D_MT_pose_apply.remove(misc_VIEW3D_MT_pose_apply.menu_func)
|
300 |
+
bpy.types.VIEW3D_PT_tools_weightpaint.remove(misc_VIEW3D_PT_tools_weightpaint.menu_func)
|
301 |
+
|
302 |
+
for pcoll in common.preview_collections.values():
|
303 |
+
bpy.utils.previews.remove(pcoll)
|
304 |
+
common.preview_collections.clear()
|
305 |
+
|
306 |
+
bpy.app.translations.unregister(__name__)
|
307 |
+
|
308 |
+
# メイン関数
|
309 |
+
if __name__ == "__main__":
|
310 |
+
register()
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/anm_export.py
ADDED
@@ -0,0 +1,334 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import bpy, mathutils
|
2 |
+
import struct, re, math, unicodedata
|
3 |
+
from . import common
|
4 |
+
|
5 |
+
# メインオペレーター
|
6 |
+
class export_cm3d2_anm(bpy.types.Operator):
|
7 |
+
bl_idname = 'export_anim.export_cm3d2_anm'
|
8 |
+
bl_label = "CM3D2 Motion (.anm) (Work In Progress)"
|
9 |
+
bl_description = "Allows you to export a pose to a .anm file."
|
10 |
+
bl_options = {'REGISTER'}
|
11 |
+
|
12 |
+
filepath = bpy.props.StringProperty(subtype='FILE_PATH')
|
13 |
+
filename_ext = ".anm"
|
14 |
+
filter_glob = bpy.props.StringProperty(default="*.anm", options={'HIDDEN'})
|
15 |
+
|
16 |
+
scale = bpy.props.FloatProperty(name="Scale", default=0.2, min=0.1, max=100, soft_min=0.1, soft_max=100, step=100, precision=1, description="Scale of the .anm at the time of export")
|
17 |
+
is_backup = bpy.props.BoolProperty(name="Backup", default=True, description="Will backup overwritten files.")
|
18 |
+
version = bpy.props.IntProperty(name="Version", default=1000, min=1000, max=1111, soft_min=1000, soft_max=1111, step=1)
|
19 |
+
|
20 |
+
frame_start = bpy.props.IntProperty(name="Starting Frame", default=0, min=0, max=99999, soft_min=0, soft_max=99999, step=1)
|
21 |
+
frame_end = bpy.props.IntProperty(name="Last Frame", default=0, min=0, max=99999, soft_min=0, soft_max=99999, step=1)
|
22 |
+
key_frame_count = bpy.props.IntProperty(name="Number of key frames", default=1, min=1, max=99999, soft_min=1, soft_max=99999, step=1)
|
23 |
+
time_scale = bpy.props.FloatProperty(name="Playback Speed", default=1.0, min=0.1, max=10.0, soft_min=0.1, soft_max=10.0, step=10, precision=1)
|
24 |
+
is_keyframe_clean = bpy.props.BoolProperty(name="Clean Keyframes", default=True)
|
25 |
+
is_smooth_handle = bpy.props.BoolProperty(name="Smooth Transitions", default=True)
|
26 |
+
|
27 |
+
items = [
|
28 |
+
('ARMATURE', "Armature", "", 'OUTLINER_OB_ARMATURE', 1),
|
29 |
+
('ARMATURE_PROPERTY', "Armature Data", "", 'ARMATURE_DATA', 2),
|
30 |
+
]
|
31 |
+
bone_parent_from = bpy.props.EnumProperty(items=items, name="Bone Parent From", default='ARMATURE_PROPERTY')
|
32 |
+
|
33 |
+
is_remove_alone_bone = bpy.props.BoolProperty(name="Remove Loose Bones", default=True)
|
34 |
+
is_remove_ik_bone = bpy.props.BoolProperty(name="Remove IK Bones", default=True)
|
35 |
+
is_remove_serial_number_bone = bpy.props.BoolProperty(name="Remove Duplicate Numbers", default=True)
|
36 |
+
is_remove_japanese_bone = bpy.props.BoolProperty(name="Remove Japanese Characters from Bones", default=True)
|
37 |
+
|
38 |
+
@classmethod
|
39 |
+
def poll(cls, context):
|
40 |
+
ob = context.active_object
|
41 |
+
if ob:
|
42 |
+
if ob.type == 'ARMATURE':
|
43 |
+
return True
|
44 |
+
return False
|
45 |
+
|
46 |
+
def invoke(self, context, event):
|
47 |
+
if common.preferences().anm_default_path:
|
48 |
+
self.filepath = common.default_cm3d2_dir(common.preferences().anm_default_path, "", "anm")
|
49 |
+
else:
|
50 |
+
self.filepath = common.default_cm3d2_dir(common.preferences().anm_export_path, "", "anm")
|
51 |
+
self.frame_start = context.scene.frame_start
|
52 |
+
self.frame_end = context.scene.frame_end
|
53 |
+
self.scale = 1.0 / common.preferences().scale
|
54 |
+
self.is_backup = bool(common.preferences().backup_ext)
|
55 |
+
self.key_frame_count = (context.scene.frame_end - context.scene.frame_start) + 1
|
56 |
+
|
57 |
+
ob = context.active_object
|
58 |
+
arm = ob.data
|
59 |
+
if "BoneData:0" in arm:
|
60 |
+
self.bone_parent_from = 'ARMATURE_PROPERTY'
|
61 |
+
else:
|
62 |
+
self.bone_parent_from = 'ARMATURE'
|
63 |
+
|
64 |
+
context.window_manager.fileselect_add(self)
|
65 |
+
return {'RUNNING_MODAL'}
|
66 |
+
|
67 |
+
def draw(self, context):
|
68 |
+
self.layout.prop(self, 'scale')
|
69 |
+
|
70 |
+
box = self.layout.box()
|
71 |
+
box.prop(self, 'is_backup', icon='FILE_BACKUP')
|
72 |
+
box.prop(self, 'version')
|
73 |
+
|
74 |
+
box = self.layout.box()
|
75 |
+
sub_box = box.box()
|
76 |
+
row = sub_box.row()
|
77 |
+
row.prop(self, 'frame_start')
|
78 |
+
row.prop(self, 'frame_end')
|
79 |
+
sub_box.prop(self, 'key_frame_count')
|
80 |
+
sub_box.prop(self, 'time_scale')
|
81 |
+
sub_box.prop(self, 'is_keyframe_clean', icon='DISCLOSURE_TRI_DOWN')
|
82 |
+
sub_box.prop(self, 'is_smooth_handle', icon='SMOOTHCURVE')
|
83 |
+
|
84 |
+
sub_box = box.box()
|
85 |
+
sub_box.label("Destination of bone parent information", icon='FILE_PARENT')
|
86 |
+
sub_box.prop(self, 'bone_parent_from', icon='FILE_PARENT', expand=True)
|
87 |
+
|
88 |
+
sub_box = box.box()
|
89 |
+
sub_box.label("Bones to Exclude", icon='X')
|
90 |
+
column = sub_box.column(align=True)
|
91 |
+
column.prop(self, 'is_remove_alone_bone', icon='UNLINKED')
|
92 |
+
column.prop(self, 'is_remove_ik_bone', icon='CONSTRAINT_BONE')
|
93 |
+
column.prop(self, 'is_remove_serial_number_bone', icon='DOTSDOWN')
|
94 |
+
column.prop(self, 'is_remove_japanese_bone', icon='MATCAP_13')
|
95 |
+
|
96 |
+
def execute(self, context):
|
97 |
+
common.preferences().anm_export_path = self.filepath
|
98 |
+
|
99 |
+
try:
|
100 |
+
file = common.open_temporary(self.filepath, 'wb', is_backup=self.is_backup)
|
101 |
+
except:
|
102 |
+
self.report(type={'ERROR'}, message="Failed to open this file, possibily inaccessible.")
|
103 |
+
return {'CANCELLED'}
|
104 |
+
|
105 |
+
try:
|
106 |
+
with file:
|
107 |
+
self.write_animation(context, file)
|
108 |
+
except common.CM3D2ExportException as e:
|
109 |
+
self.report(type={'ERROR'}, message=str(e))
|
110 |
+
return {'CANCELLED'}
|
111 |
+
|
112 |
+
return {'FINISHED'}
|
113 |
+
|
114 |
+
def write_animation(self, context, file):
|
115 |
+
ob = context.active_object
|
116 |
+
arm = ob.data
|
117 |
+
pose = ob.pose
|
118 |
+
fps = context.scene.render.fps
|
119 |
+
|
120 |
+
common.write_str(file, 'CM3D2_ANIM')
|
121 |
+
file.write(struct.pack('<i', self.version))
|
122 |
+
|
123 |
+
bone_parents = {}
|
124 |
+
if self.bone_parent_from == 'ARMATURE_PROPERTY':
|
125 |
+
for i in range(9999):
|
126 |
+
name = "BoneData:" + str(i)
|
127 |
+
if name not in arm: continue
|
128 |
+
elems = arm[name].split(",")
|
129 |
+
if len(elems) != 5: continue
|
130 |
+
if elems[0] in arm.bones:
|
131 |
+
if elems[2] in arm.bones:
|
132 |
+
bone_parents[elems[0]] = arm.bones[elems[2]]
|
133 |
+
else:
|
134 |
+
bone_parents[elems[0]] = None
|
135 |
+
for bone in arm.bones:
|
136 |
+
if bone.name in bone_parents: continue
|
137 |
+
bone_parents[bone.name] = bone.parent
|
138 |
+
else:
|
139 |
+
for bone in arm.bones:
|
140 |
+
bone_parents[bone.name] = bone.parent
|
141 |
+
|
142 |
+
def is_japanese(string):
|
143 |
+
for ch in string:
|
144 |
+
name = unicodedata.name(ch)
|
145 |
+
if "CJK UNIFIED" in name \
|
146 |
+
or "HIRAGANA" in name \
|
147 |
+
or "KATAKANA" in name:
|
148 |
+
return True
|
149 |
+
return False
|
150 |
+
bones = []
|
151 |
+
already_bone_names = []
|
152 |
+
bones_queue = arm.bones[:]
|
153 |
+
while len(bones_queue):
|
154 |
+
bone = bones_queue.pop(0)
|
155 |
+
|
156 |
+
if not bone_parents[bone.name]:
|
157 |
+
already_bone_names.append(bone.name)
|
158 |
+
if self.is_remove_serial_number_bone:
|
159 |
+
if re.search(r"\.\d{3,}$", bone.name): continue
|
160 |
+
if self.is_remove_japanese_bone:
|
161 |
+
if is_japanese(bone.name): continue
|
162 |
+
if self.is_remove_alone_bone and len(bone.children) == 0:
|
163 |
+
continue
|
164 |
+
bones.append(bone)
|
165 |
+
continue
|
166 |
+
elif bone_parents[bone.name].name in already_bone_names:
|
167 |
+
already_bone_names.append(bone.name)
|
168 |
+
if self.is_remove_serial_number_bone:
|
169 |
+
if re.search(r"\.\d{3,}$", bone.name): continue
|
170 |
+
if self.is_remove_japanese_bone:
|
171 |
+
if is_japanese(bone.name): continue
|
172 |
+
if self.is_remove_ik_bone:
|
173 |
+
if "_ik_" in bone.name.lower(): continue
|
174 |
+
if re.search(r"_nub$", bone.name.lower()): continue
|
175 |
+
if re.search(r"Nub$", bone.name): continue
|
176 |
+
bones.append(bone)
|
177 |
+
continue
|
178 |
+
|
179 |
+
bones_queue.append(bone)
|
180 |
+
|
181 |
+
anm_data_raw = {}
|
182 |
+
class KeyFrame:
|
183 |
+
def __init__(self, time, value):
|
184 |
+
self.time = time
|
185 |
+
self.value = value
|
186 |
+
same_locs = {}
|
187 |
+
same_rots = {}
|
188 |
+
pre_rots = {}
|
189 |
+
for key_frame_index in range(self.key_frame_count):
|
190 |
+
if self.key_frame_count == 1:
|
191 |
+
frame = 0.0
|
192 |
+
else:
|
193 |
+
frame = (self.frame_end - self.frame_start) / (self.key_frame_count - 1) * key_frame_index + self.frame_start
|
194 |
+
context.scene.frame_set(int(frame), frame - int(frame))
|
195 |
+
context.scene.update()
|
196 |
+
|
197 |
+
time = frame / fps * (1.0 / self.time_scale)
|
198 |
+
|
199 |
+
for bone in bones:
|
200 |
+
if bone.name not in anm_data_raw:
|
201 |
+
anm_data_raw[bone.name] = {"LOC":{}, "ROT":{}}
|
202 |
+
same_locs[bone.name] = []
|
203 |
+
same_rots[bone.name] = []
|
204 |
+
|
205 |
+
pose_bone = pose.bones[bone.name]
|
206 |
+
|
207 |
+
pose_mat = ob.convert_space(pose_bone, pose_bone.matrix, 'POSE', 'WORLD')
|
208 |
+
if bone_parents[bone.name]:
|
209 |
+
parent_mat = ob.convert_space(pose.bones[bone_parents[bone.name].name], pose.bones[bone_parents[bone.name].name].matrix, 'POSE', 'WORLD')
|
210 |
+
pose_mat = parent_mat.inverted() * pose_mat
|
211 |
+
|
212 |
+
loc = pose_mat.to_translation() * self.scale
|
213 |
+
rot = pose_mat.to_quaternion()
|
214 |
+
|
215 |
+
if bone.name in pre_rots:
|
216 |
+
if 5.0 < pre_rots[bone.name].rotation_difference(rot).angle:
|
217 |
+
rot.w, rot.x, rot.y, rot.z = -rot.w, -rot.x, -rot.y, -rot.z
|
218 |
+
pre_rots[bone.name] = rot.copy()
|
219 |
+
|
220 |
+
if bone_parents[bone.name]:
|
221 |
+
loc.x, loc.y, loc.z = -loc.y, -loc.x, loc.z
|
222 |
+
rot.w, rot.x, rot.y, rot.z = rot.w, rot.y, rot.x, -rot.z
|
223 |
+
else:
|
224 |
+
loc.x, loc.y, loc.z = -loc.x, loc.z, -loc.y
|
225 |
+
|
226 |
+
fix_quat = mathutils.Euler((0, 0, math.radians(-90)), 'XYZ').to_quaternion()
|
227 |
+
fix_quat2 = mathutils.Euler((math.radians(-90), 0, 0), 'XYZ').to_quaternion()
|
228 |
+
rot = rot * fix_quat * fix_quat2
|
229 |
+
|
230 |
+
rot.w, rot.x, rot.y, rot.z = -rot.y, -rot.z, -rot.x, rot.w
|
231 |
+
|
232 |
+
if not self.is_keyframe_clean or key_frame_index == 0 or key_frame_index == self.key_frame_count - 1:
|
233 |
+
anm_data_raw[bone.name]["LOC"][time] = loc.copy()
|
234 |
+
anm_data_raw[bone.name]["ROT"][time] = rot.copy()
|
235 |
+
|
236 |
+
if self.is_keyframe_clean:
|
237 |
+
same_locs[bone.name].append(KeyFrame(time, loc.copy()))
|
238 |
+
same_rots[bone.name].append(KeyFrame(time, rot.copy()))
|
239 |
+
else:
|
240 |
+
def is_mismatch(a, b):
|
241 |
+
return 0.000001 < abs(a - b)
|
242 |
+
|
243 |
+
a, b = loc, same_locs[bone.name][-1].value
|
244 |
+
if is_mismatch(a.x, b.x) or is_mismatch(a.y, b.y) or is_mismatch(a.z, b.z):
|
245 |
+
if 2 <= len(same_locs[bone.name]):
|
246 |
+
anm_data_raw[bone.name]["LOC"][same_locs[bone.name][-1].time] = same_locs[bone.name][-1].value.copy()
|
247 |
+
anm_data_raw[bone.name]["LOC"][time] = loc.copy()
|
248 |
+
same_locs[bone.name] = [KeyFrame(time, loc.copy())]
|
249 |
+
else:
|
250 |
+
same_locs[bone.name].append(KeyFrame(time, loc.copy()))
|
251 |
+
|
252 |
+
a, b = rot, same_rots[bone.name][-1].value
|
253 |
+
if is_mismatch(a.w, b.w) or is_mismatch(a.x, b.x) or is_mismatch(a.y, b.y) or is_mismatch(a.z, b.z):
|
254 |
+
if 2 <= len(same_rots[bone.name]):
|
255 |
+
anm_data_raw[bone.name]["ROT"][same_rots[bone.name][-1].time] = same_rots[bone.name][-1].value.copy()
|
256 |
+
anm_data_raw[bone.name]["ROT"][time] = rot.copy()
|
257 |
+
same_rots[bone.name] = [KeyFrame(time, rot.copy())]
|
258 |
+
else:
|
259 |
+
same_rots[bone.name].append(KeyFrame(time, rot.copy()))
|
260 |
+
|
261 |
+
anm_data = {}
|
262 |
+
for bone_name, channels in anm_data_raw.items():
|
263 |
+
anm_data[bone_name] = {100:{}, 101:{}, 102:{}, 103:{}, 104:{}, 105:{}, 106:{}}
|
264 |
+
for time, loc in channels["LOC"].items():
|
265 |
+
anm_data[bone_name][104][time] = loc.x
|
266 |
+
anm_data[bone_name][105][time] = loc.y
|
267 |
+
anm_data[bone_name][106][time] = loc.z
|
268 |
+
for time, rot in channels["ROT"].items():
|
269 |
+
anm_data[bone_name][100][time] = rot.x
|
270 |
+
anm_data[bone_name][101][time] = rot.y
|
271 |
+
anm_data[bone_name][102][time] = rot.z
|
272 |
+
anm_data[bone_name][103][time] = rot.w
|
273 |
+
|
274 |
+
for bone in bones:
|
275 |
+
file.write(struct.pack('<?', True))
|
276 |
+
|
277 |
+
bone_names = [bone.name]
|
278 |
+
current_bone = bone
|
279 |
+
while bone_parents[current_bone.name]:
|
280 |
+
bone_names.append(bone_parents[current_bone.name].name)
|
281 |
+
current_bone = bone_parents[current_bone.name]
|
282 |
+
|
283 |
+
bone_names.reverse()
|
284 |
+
common.write_str(file, "/".join(bone_names))
|
285 |
+
|
286 |
+
for channel_id, keyframes in sorted(anm_data[bone.name].items(), key=lambda x: x[0]):
|
287 |
+
file.write(struct.pack('<B', channel_id))
|
288 |
+
file.write(struct.pack('<i', len(keyframes)))
|
289 |
+
|
290 |
+
keyframes_list = sorted(keyframes.items(), key=lambda x: x[0])
|
291 |
+
for i in range(len(keyframes_list)):
|
292 |
+
x = keyframes_list[i][0]
|
293 |
+
y = keyframes_list[i][1]
|
294 |
+
|
295 |
+
if len(keyframes_list) <= 1:
|
296 |
+
file.write(struct.pack('<f', x))
|
297 |
+
file.write(struct.pack('<f', y))
|
298 |
+
file.write(struct.pack('<2f', 0.0, 0.0))
|
299 |
+
continue
|
300 |
+
|
301 |
+
if i == 0:
|
302 |
+
prev_x = x - (keyframes_list[i+1][0] - x)
|
303 |
+
prev_y = y - (keyframes_list[i+1][1] - y)
|
304 |
+
next_x = keyframes_list[i+1][0]
|
305 |
+
next_y = keyframes_list[i+1][1]
|
306 |
+
elif i == len(keyframes_list) - 1:
|
307 |
+
prev_x = keyframes_list[i-1][0]
|
308 |
+
prev_y = keyframes_list[i-1][1]
|
309 |
+
next_x = x + (x - keyframes_list[i-1][0])
|
310 |
+
next_y = y + (y - keyframes_list[i-1][1])
|
311 |
+
else:
|
312 |
+
prev_x = keyframes_list[i-1][0]
|
313 |
+
prev_y = keyframes_list[i-1][1]
|
314 |
+
next_x = keyframes_list[i+1][0]
|
315 |
+
next_y = keyframes_list[i+1][1]
|
316 |
+
|
317 |
+
prev_rad = (prev_y - y) / (prev_x - x)
|
318 |
+
next_rad = (next_y - y) / (next_x - x)
|
319 |
+
join_rad = (prev_rad + next_rad) / 2
|
320 |
+
|
321 |
+
file.write(struct.pack('<f', x))
|
322 |
+
file.write(struct.pack('<f', y))
|
323 |
+
|
324 |
+
if self.is_smooth_handle:
|
325 |
+
file.write(struct.pack('<2f', join_rad, join_rad))
|
326 |
+
#file.write(struct.pack('<2f', prev_rad, next_rad))
|
327 |
+
else:
|
328 |
+
file.write(struct.pack('<2f', 0.0, 0.0))
|
329 |
+
|
330 |
+
file.write(struct.pack('<?', False))
|
331 |
+
|
332 |
+
# メニューに登録する関数
|
333 |
+
def menu_func(self, context):
|
334 |
+
self.layout.operator(export_cm3d2_anm.bl_idname, icon_value=common.preview_collections['main']['KISS'].icon_id)
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/anm_import.py
ADDED
@@ -0,0 +1,213 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os, re, bpy, math, struct, os.path, mathutils
|
2 |
+
from . import common
|
3 |
+
|
4 |
+
# メインオペレーター
|
5 |
+
class import_cm3d2_anm(bpy.types.Operator):
|
6 |
+
bl_idname = 'import_anim.import_cm3d2_anm'
|
7 |
+
bl_label = "CM3D2 Animation (.anm)"
|
8 |
+
bl_description = "Loads a CM3D2 .anm file."
|
9 |
+
bl_options = {'REGISTER'}
|
10 |
+
|
11 |
+
filepath = bpy.props.StringProperty(subtype='FILE_PATH')
|
12 |
+
filename_ext = ".anm"
|
13 |
+
filter_glob = bpy.props.StringProperty(default="*.anm", options={'HIDDEN'})
|
14 |
+
|
15 |
+
scale = bpy.props.FloatProperty(name="Scale", default=5, min=0.1, max=100, soft_min=0.1, soft_max=100, step=100, precision=1, description="The scale at the time of import.")
|
16 |
+
|
17 |
+
remove_pre_animation = bpy.props.BoolProperty(name="Remove previous Animation", default=True)
|
18 |
+
set_frame = bpy.props.BoolProperty(name="Set Frame", default=True)
|
19 |
+
ignore_automatic_bone = bpy.props.BoolProperty(name="Exclude Twister Bones", default=True)
|
20 |
+
|
21 |
+
is_location = bpy.props.BoolProperty(name="Position", default=True)
|
22 |
+
is_rotation = bpy.props.BoolProperty(name="Rotation", default=True)
|
23 |
+
is_scale = bpy.props.BoolProperty(name="Bigger/Smaller", default=False)
|
24 |
+
|
25 |
+
|
26 |
+
@classmethod
|
27 |
+
def poll(cls, context):
|
28 |
+
ob = context.active_object
|
29 |
+
if ob:
|
30 |
+
if ob.type == 'ARMATURE':
|
31 |
+
return True
|
32 |
+
return False
|
33 |
+
|
34 |
+
def invoke(self, context, event):
|
35 |
+
if common.preferences().anm_default_path:
|
36 |
+
self.filepath = common.default_cm3d2_dir(common.preferences().anm_default_path, "", "anm")
|
37 |
+
else:
|
38 |
+
self.filepath = common.default_cm3d2_dir(common.preferences().anm_import_path, "", "anm")
|
39 |
+
self.scale = common.preferences().scale
|
40 |
+
context.window_manager.fileselect_add(self)
|
41 |
+
return {'RUNNING_MODAL'}
|
42 |
+
|
43 |
+
def draw(self, context):
|
44 |
+
self.layout.prop(self, 'scale')
|
45 |
+
box = self.layout.box()
|
46 |
+
box.prop(self, 'remove_pre_animation', icon='DISCLOSURE_TRI_DOWN')
|
47 |
+
box.prop(self, 'set_frame', icon='NEXT_KEYFRAME')
|
48 |
+
box.prop(self, 'ignore_automatic_bone', icon='X')
|
49 |
+
box = self.layout.box()
|
50 |
+
box.label("Animation Information")
|
51 |
+
column = box.column(align=True)
|
52 |
+
column.prop(self, 'is_location', icon='MAN_TRANS')
|
53 |
+
column.prop(self, 'is_rotation', icon='MAN_ROT')
|
54 |
+
row = column.row()
|
55 |
+
row.prop(self, 'is_scale', icon='MAN_SCALE')
|
56 |
+
row.enabled = False
|
57 |
+
|
58 |
+
def execute(self, context):
|
59 |
+
common.preferences().anm_import_path = self.filepath
|
60 |
+
|
61 |
+
try:
|
62 |
+
file = open(self.filepath, 'rb')
|
63 |
+
except:
|
64 |
+
self.report(type={'ERROR'}, message="Failed to open the file. It's either inaccessible or the file does not exist")
|
65 |
+
return {'CANCELLED'}
|
66 |
+
|
67 |
+
# ヘッダー
|
68 |
+
ext = common.read_str(file)
|
69 |
+
if ext != 'CM3D2_ANIM':
|
70 |
+
self.report(type={'ERROR'}, message="This is not a CM3D2 animation file.")
|
71 |
+
return {'CANCELLED'}
|
72 |
+
struct.unpack('<i', file.read(4))[0]
|
73 |
+
|
74 |
+
global_flag = struct.unpack('<?', file.read(1))[0]
|
75 |
+
|
76 |
+
anm_data = {}
|
77 |
+
|
78 |
+
for anm_data_index in range(9**9):
|
79 |
+
path = common.read_str(file)
|
80 |
+
|
81 |
+
base_bone_name = path.split('/')[-1]
|
82 |
+
if base_bone_name not in anm_data:
|
83 |
+
anm_data[base_bone_name] = {'path':path}
|
84 |
+
anm_data[base_bone_name]['channels'] = {}
|
85 |
+
|
86 |
+
for channel_index in range(9**9):
|
87 |
+
channel_id = struct.unpack('<B', file.read(1))[0]
|
88 |
+
channel_id_str = channel_id
|
89 |
+
if channel_id <= 1:
|
90 |
+
break
|
91 |
+
anm_data[base_bone_name]['channels'][channel_id_str] = []
|
92 |
+
channel_data_count = struct.unpack('<i', file.read(4))[0]
|
93 |
+
for channel_data_index in range(channel_data_count):
|
94 |
+
frame = struct.unpack('<f', file.read(4))[0]
|
95 |
+
data = struct.unpack('<3f', file.read(4*3))
|
96 |
+
|
97 |
+
anm_data[base_bone_name]['channels'][channel_id_str].append({'frame':frame, 'f0':data[0], 'f1':data[1], 'f2':data[2]})
|
98 |
+
|
99 |
+
if channel_id == 0:
|
100 |
+
break
|
101 |
+
|
102 |
+
fps = context.scene.render.fps
|
103 |
+
|
104 |
+
ob = context.active_object
|
105 |
+
arm = ob.data
|
106 |
+
pose = ob.pose
|
107 |
+
|
108 |
+
if self.remove_pre_animation:
|
109 |
+
anim = ob.animation_data
|
110 |
+
if anim:
|
111 |
+
if anim.action:
|
112 |
+
for fcurve in anim.action.fcurves:
|
113 |
+
anim.action.fcurves.remove(fcurve)
|
114 |
+
|
115 |
+
max_frame = 0
|
116 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
117 |
+
for bone_name, bone_data in anm_data.items():
|
118 |
+
|
119 |
+
if self.ignore_automatic_bone:
|
120 |
+
if re.match(r"Kata_[RL]", bone_name): continue
|
121 |
+
if re.match(r"Uppertwist1_[RL]", bone_name): continue
|
122 |
+
if re.match(r"momoniku_[RL]", bone_name): continue
|
123 |
+
|
124 |
+
if bone_name not in pose.bones:
|
125 |
+
bone_name = common.decode_bone_name(bone_name)
|
126 |
+
if bone_name not in pose.bones:
|
127 |
+
continue
|
128 |
+
bone = arm.bones[bone_name]
|
129 |
+
pose_bone = pose.bones[bone_name]
|
130 |
+
|
131 |
+
locs = {}
|
132 |
+
quats = {}
|
133 |
+
for channel_id, channel_data in bone_data['channels'].items():
|
134 |
+
|
135 |
+
if channel_id in [100, 101, 102, 103]:
|
136 |
+
for data in channel_data:
|
137 |
+
frame = data['frame']
|
138 |
+
if frame not in quats:
|
139 |
+
quats[frame] = [None, None, None, None]
|
140 |
+
|
141 |
+
if channel_id == 103:
|
142 |
+
quats[frame][0] = data['f0']
|
143 |
+
elif channel_id == 100:
|
144 |
+
quats[frame][1] = data['f0']
|
145 |
+
elif channel_id == 101:
|
146 |
+
quats[frame][2] = data['f0']
|
147 |
+
elif channel_id == 102:
|
148 |
+
quats[frame][3] = data['f0']
|
149 |
+
|
150 |
+
elif channel_id in [104, 105, 106]:
|
151 |
+
for data in channel_data:
|
152 |
+
frame = data['frame']
|
153 |
+
if frame not in locs:
|
154 |
+
locs[frame] = [None, None, None]
|
155 |
+
|
156 |
+
if channel_id == 104:
|
157 |
+
locs[frame][0] = data['f0']
|
158 |
+
elif channel_id == 105:
|
159 |
+
locs[frame][1] = data['f0']
|
160 |
+
elif channel_id == 106:
|
161 |
+
locs[frame][2] = data['f0']
|
162 |
+
|
163 |
+
if self.is_location:
|
164 |
+
for frame, loc in locs.items():
|
165 |
+
loc = mathutils.Vector(loc) * self.scale
|
166 |
+
bone_loc = bone.head_local.copy()
|
167 |
+
|
168 |
+
if bone.parent:
|
169 |
+
loc.x, loc.y, loc.z = -loc.y, -loc.x, loc.z
|
170 |
+
|
171 |
+
bone_loc = bone_loc - bone.parent.head_local
|
172 |
+
bone_loc.rotate(bone.parent.matrix_local.to_quaternion().inverted())
|
173 |
+
else:
|
174 |
+
loc.x, loc.y, loc.z = loc.x, loc.z, loc.y
|
175 |
+
|
176 |
+
result_loc = loc - bone_loc
|
177 |
+
pose_bone.location = result_loc.copy()
|
178 |
+
|
179 |
+
pose_bone.keyframe_insert('location', frame=frame * fps)
|
180 |
+
if max_frame < frame * fps:
|
181 |
+
max_frame = frame * fps
|
182 |
+
|
183 |
+
if self.is_rotation:
|
184 |
+
for frame, quat in quats.items():
|
185 |
+
quat = mathutils.Quaternion(quat)
|
186 |
+
bone_quat = bone.matrix.to_quaternion()
|
187 |
+
|
188 |
+
if bone.parent:
|
189 |
+
quat.w, quat.x, quat.y, quat.z = quat.w, quat.y, quat.x, -quat.z
|
190 |
+
else:
|
191 |
+
quat.w, quat.x, quat.y, quat.z = quat.w, quat.y, quat.x, -quat.z
|
192 |
+
|
193 |
+
fix_quat = mathutils.Euler((math.radians(90), math.radians(90), 0.0), 'XYZ').to_quaternion()
|
194 |
+
fix_quat2 = mathutils.Euler((0.0, math.radians(-90), 0.0), 'XYZ').to_quaternion()
|
195 |
+
quat = fix_quat * quat
|
196 |
+
|
197 |
+
result_quat = bone_quat.inverted() * quat
|
198 |
+
pose_bone.rotation_quaternion = result_quat.copy()
|
199 |
+
|
200 |
+
pose_bone.keyframe_insert('rotation_quaternion', frame=frame * fps)
|
201 |
+
if max_frame < frame * fps:
|
202 |
+
max_frame = frame * fps
|
203 |
+
|
204 |
+
if self.set_frame:
|
205 |
+
context.scene.frame_start = 0
|
206 |
+
context.scene.frame_end = max_frame
|
207 |
+
context.scene.frame_set(0)
|
208 |
+
|
209 |
+
return {'FINISHED'}
|
210 |
+
|
211 |
+
# メニューに登録する関数
|
212 |
+
def menu_func(self, context):
|
213 |
+
self.layout.operator(import_cm3d2_anm.bl_idname, icon_value=common.preview_collections['main']['KISS'].icon_id)
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/append_data.blend
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:beea77137dea25c59f59e6865dda2f37d3f5810630e76cf28cefbf510bca20e7
|
3 |
+
size 26923656
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/common.py
ADDED
@@ -0,0 +1,635 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import bpy, os, re, math, bmesh, struct, shutil, mathutils
|
2 |
+
from . import fileutil
|
3 |
+
|
4 |
+
# アドオン情報
|
5 |
+
bl_info = {
|
6 |
+
"name" : "CM3D2 Converter",
|
7 |
+
"author" : "@saidenka_cm3d2",
|
8 |
+
"version" : (2017, 5, 16, 21, 39, 32),
|
9 |
+
"blender" : (2, 78, 0),
|
10 |
+
"location" : "File> Import / Export > CM3D2 Model (.model)",
|
11 |
+
"description" : "Import/Export .Model files for CM3D2",
|
12 |
+
"warning" : "",
|
13 |
+
"wiki_url" : "https://github.com/CM3Duser/Blender-CM3D2-Converter",
|
14 |
+
"tracker_url" : "https://twitter.com/saidenka_cm3d2",
|
15 |
+
"category" : "Import-Export"
|
16 |
+
}
|
17 |
+
|
18 |
+
addon_name = "CM3D2 Converter"
|
19 |
+
preview_collections = {}
|
20 |
+
|
21 |
+
# このアドオンの設定値群を呼び出す
|
22 |
+
def preferences():
|
23 |
+
return bpy.context.user_preferences.addons[__name__.split('.')[0]].preferences
|
24 |
+
|
25 |
+
# データ名末尾の「.001」などを削除
|
26 |
+
def remove_serial_number(name, enable=True):
|
27 |
+
return re.sub(r'\.\d{3,}$', "", name) if enable else name
|
28 |
+
|
29 |
+
# 文字列の左右端から空白を削除
|
30 |
+
def line_trim(line, enable=True):
|
31 |
+
return line.strip(' \t\r\n') if enable else line
|
32 |
+
|
33 |
+
# CM3D2専用ファイル用の文字列書き込み
|
34 |
+
def write_str(file, raw_str):
|
35 |
+
b_str = format(len(raw_str.encode('utf-8')), 'b')
|
36 |
+
for i in range(9):
|
37 |
+
if 7 < len(b_str):
|
38 |
+
file.write( struct.pack('<B', int("1"+b_str[-7:], 2)) )
|
39 |
+
b_str = b_str[:-7]
|
40 |
+
else:
|
41 |
+
file.write( struct.pack('<B', int(b_str, 2)) )
|
42 |
+
break
|
43 |
+
file.write(raw_str.encode('utf-8'))
|
44 |
+
|
45 |
+
# CM3D2専用ファイル用の文字列読み込み
|
46 |
+
def read_str(file, total_b = ""):
|
47 |
+
for i in range(9):
|
48 |
+
b_str = format(struct.unpack('<B', file.read(1))[0], '08b')
|
49 |
+
total_b = b_str[1:] + total_b
|
50 |
+
if b_str[0] == '0': break
|
51 |
+
return file.read(int(total_b, 2)).decode('utf-8')
|
52 |
+
|
53 |
+
# ボーン/ウェイト名を Blender → CM3D2
|
54 |
+
def encode_bone_name(name, enable=True):
|
55 |
+
return re.sub(r'([_ ])\*([_ ].*)\.([rRlL])$', r'\1\3\2', name) if name.count('*') == 1 and enable else name
|
56 |
+
|
57 |
+
# ボーン/ウェイト名を CM3D2 → Blender
|
58 |
+
def decode_bone_name(name, enable=True):
|
59 |
+
return re.sub(r'([_ ])([rRlL])([_ ].*)$', r'\1*\3.\2', name) if enable else name
|
60 |
+
|
61 |
+
# CM3D2用マテリアルを設定に合わせて装飾
|
62 |
+
def decorate_material(mate, enable=True, me=None, mate_index=-1):
|
63 |
+
if not enable: return
|
64 |
+
if 'shader1' not in mate: return
|
65 |
+
|
66 |
+
shader = mate['shader1']
|
67 |
+
if 'CM3D2/Man' == shader:
|
68 |
+
mate.use_shadeless = True
|
69 |
+
mate.diffuse_color = (0, 1, 1)
|
70 |
+
elif 'CM3D2/Mosaic' == shader:
|
71 |
+
mate.use_transparency = True
|
72 |
+
mate.transparency_method = 'RAYTRACE'
|
73 |
+
mate.alpha = 0.25
|
74 |
+
mate.raytrace_transparency.ior = 2
|
75 |
+
elif 'CM3D2_Debug/Debug_CM3D2_Normal2Color' == shader:
|
76 |
+
mate.use_tangent_shading = True
|
77 |
+
mate.diffuse_color = (0.5, 0.5, 1)
|
78 |
+
|
79 |
+
else:
|
80 |
+
if '/Toony_' in shader:
|
81 |
+
mate.diffuse_shader = 'TOON'
|
82 |
+
mate.diffuse_toon_smooth = 0.01
|
83 |
+
mate.diffuse_toon_size = 1.2
|
84 |
+
if 'Trans' in shader:
|
85 |
+
mate.use_transparency = True
|
86 |
+
mate.alpha = 0.0
|
87 |
+
mate.texture_slots[0].use_map_alpha = True
|
88 |
+
if 'Unlit/' in shader:
|
89 |
+
mate.emit = 0.5
|
90 |
+
if '_NoZ' in shader:
|
91 |
+
mate.offset_z = 9999
|
92 |
+
|
93 |
+
is_colored = False
|
94 |
+
is_textured = [False, False, False, False]
|
95 |
+
rimcolor, rimpower, rimshift = mathutils.Color((1, 1, 1)), 0.0, 0.0
|
96 |
+
for slot in mate.texture_slots:
|
97 |
+
if not slot: continue
|
98 |
+
if not slot.texture: continue
|
99 |
+
|
100 |
+
tex = slot.texture
|
101 |
+
tex_name = remove_serial_number(tex.name)
|
102 |
+
slot.use_map_color_diffuse = False
|
103 |
+
|
104 |
+
if tex_name == '_MainTex':
|
105 |
+
slot.use_map_color_diffuse = True
|
106 |
+
if 'image' in dir(tex):
|
107 |
+
img = tex.image
|
108 |
+
if len(img.pixels):
|
109 |
+
if me:
|
110 |
+
color = mathutils.Color(get_image_average_color_uv(img, me, mate_index)[:3])
|
111 |
+
else:
|
112 |
+
color = mathutils.Color(get_image_average_color(img)[:3])
|
113 |
+
mate.diffuse_color = color
|
114 |
+
is_colored = True
|
115 |
+
|
116 |
+
elif tex_name == '_RimColor':
|
117 |
+
rimcolor = slot.color[:]
|
118 |
+
if not is_colored:
|
119 |
+
mate.diffuse_color = slot.color[:]
|
120 |
+
mate.diffuse_color.v += 0.5
|
121 |
+
|
122 |
+
elif tex_name == '_Shininess':
|
123 |
+
mate.specular_intensity = slot.diffuse_color_factor
|
124 |
+
|
125 |
+
elif tex_name == '_RimPower':
|
126 |
+
rimpower = slot.diffuse_color_factor
|
127 |
+
|
128 |
+
elif tex_name == '_RimShift':
|
129 |
+
rimshift = slot.diffuse_color_factor
|
130 |
+
|
131 |
+
for index, name in enumerate(['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon']):
|
132 |
+
if tex_name == name:
|
133 |
+
if 'image' in dir(tex):
|
134 |
+
if tex.image:
|
135 |
+
if len(tex.image.pixels):
|
136 |
+
is_textured[index] = tex
|
137 |
+
|
138 |
+
set_texture_color(slot)
|
139 |
+
|
140 |
+
# よりオリジナルに近く描画するノード作成
|
141 |
+
if all(is_textured):
|
142 |
+
mate.use_nodes = True
|
143 |
+
mate.use_shadeless = True
|
144 |
+
|
145 |
+
node_tree = mate.node_tree
|
146 |
+
for node in node_tree.nodes[:]:
|
147 |
+
node_tree.nodes.remove(node)
|
148 |
+
|
149 |
+
mate_node = node_tree.nodes.new('ShaderNodeExtendedMaterial')
|
150 |
+
mate_node.location = (0, 0)
|
151 |
+
mate_node.material = mate
|
152 |
+
|
153 |
+
if "CM3D2 Shade" in bpy.context.blend_data.materials:
|
154 |
+
shade_mate = bpy.context.blend_data.materials["CM3D2 Shade"]
|
155 |
+
else:
|
156 |
+
shade_mate = bpy.context.blend_data.materials.new("CM3D2 Shade")
|
157 |
+
shade_mate.diffuse_color = (1, 1, 1)
|
158 |
+
shade_mate.diffuse_intensity = 1
|
159 |
+
shade_mate.specular_intensity = 1
|
160 |
+
shade_mate_node = node_tree.nodes.new('ShaderNodeExtendedMaterial')
|
161 |
+
shade_mate_node.location = (234.7785, -131.8243)
|
162 |
+
shade_mate_node.material = shade_mate
|
163 |
+
|
164 |
+
toon_node = node_tree.nodes.new('ShaderNodeValToRGB')
|
165 |
+
toon_node.location = (571.3662, -381.0965)
|
166 |
+
toon_img = is_textured[1].image
|
167 |
+
toon_w, toon_h = toon_img.size[0], toon_img.size[1]
|
168 |
+
for i in range(32 - 2):
|
169 |
+
toon_node.color_ramp.elements.new(0.0)
|
170 |
+
for i in range(32):
|
171 |
+
pos = i / (32 - 1)
|
172 |
+
toon_node.color_ramp.elements[i].position = pos
|
173 |
+
x = int( (toon_w / (32 - 1)) * i )
|
174 |
+
pixel_index = x * toon_img.channels
|
175 |
+
toon_node.color_ramp.elements[i].color = toon_img.pixels[pixel_index:pixel_index+4]
|
176 |
+
toon_node.color_ramp.interpolation = 'EASE'
|
177 |
+
|
178 |
+
shadow_rate_node = node_tree.nodes.new('ShaderNodeValToRGB')
|
179 |
+
shadow_rate_node.location = (488.2785, 7.8446)
|
180 |
+
shadow_rate_img = is_textured[3].image
|
181 |
+
shadow_rate_w, shadow_rate_h = shadow_rate_img.size[0], shadow_rate_img.size[1]
|
182 |
+
for i in range(32 - 2):
|
183 |
+
shadow_rate_node.color_ramp.elements.new(0.0)
|
184 |
+
for i in range(32):
|
185 |
+
pos = i / (32 - 1)
|
186 |
+
shadow_rate_node.color_ramp.elements[i].position = pos
|
187 |
+
x = int( (shadow_rate_w / (32)) * i )
|
188 |
+
pixel_index = x * shadow_rate_img.channels
|
189 |
+
shadow_rate_node.color_ramp.elements[i].color = shadow_rate_img.pixels[pixel_index:pixel_index+4]
|
190 |
+
shadow_rate_node.color_ramp.interpolation = 'EASE'
|
191 |
+
|
192 |
+
geometry_node = node_tree.nodes.new('ShaderNodeGeometry')
|
193 |
+
geometry_node.location = (323.4597, -810.8045)
|
194 |
+
|
195 |
+
shadow_texture_node = node_tree.nodes.new('ShaderNodeTexture')
|
196 |
+
shadow_texture_node.location = (626.0117, -666.0227)
|
197 |
+
shadow_texture_node.texture = is_textured[2]
|
198 |
+
|
199 |
+
invert_node = node_tree.nodes.new('ShaderNodeInvert')
|
200 |
+
invert_node.location = (805.6814, -132.9144)
|
201 |
+
|
202 |
+
shadow_mix_node = node_tree.nodes.new('ShaderNodeMixRGB')
|
203 |
+
shadow_mix_node.location = (1031.2714, -201.5598)
|
204 |
+
|
205 |
+
toon_mix_node = node_tree.nodes.new('ShaderNodeMixRGB')
|
206 |
+
toon_mix_node.location = (1257.5538, -308.8037)
|
207 |
+
toon_mix_node.blend_type = 'MULTIPLY'
|
208 |
+
toon_mix_node.inputs[0].default_value = 1.0
|
209 |
+
|
210 |
+
specular_mix_node = node_tree.nodes.new('ShaderNodeMixRGB')
|
211 |
+
specular_mix_node.location = (1473.2079, -382.7421)
|
212 |
+
specular_mix_node.blend_type = 'SCREEN'
|
213 |
+
specular_mix_node.inputs[0].default_value = mate.specular_intensity
|
214 |
+
|
215 |
+
normal_node = node_tree.nodes.new('ShaderNodeNormal')
|
216 |
+
normal_node.location = (912.1372, -590.8748)
|
217 |
+
|
218 |
+
rim_ramp_node = node_tree.nodes.new('ShaderNodeValToRGB')
|
219 |
+
rim_ramp_node.location = (1119.0664, -570.0284)
|
220 |
+
rim_ramp_node.color_ramp.elements[0].color = list(rimcolor[:]) + [1.0]
|
221 |
+
rim_ramp_node.color_ramp.elements[0].position = rimshift
|
222 |
+
rim_ramp_node.color_ramp.elements[1].color = (0, 0, 0, 1)
|
223 |
+
rim_ramp_node.color_ramp.elements[1].position = (rimshift) + ((1.0 - (rimpower * 0.03333)) * 0.5)
|
224 |
+
|
225 |
+
rim_power_node = node_tree.nodes.new('ShaderNodeHueSaturation')
|
226 |
+
rim_power_node.location = (1426.6332, -575.6142)
|
227 |
+
#rim_power_node.inputs[2].default_value = rimpower * 0.1
|
228 |
+
|
229 |
+
rim_mix_node = node_tree.nodes.new('ShaderNodeMixRGB')
|
230 |
+
rim_mix_node.location = (1724.7024, -451.9624)
|
231 |
+
rim_mix_node.blend_type = 'ADD'
|
232 |
+
|
233 |
+
out_node = node_tree.nodes.new('ShaderNodeOutput')
|
234 |
+
out_node.location = (1957.4023, -480.5365)
|
235 |
+
|
236 |
+
node_tree.links.new(shadow_mix_node.inputs[1], mate_node.outputs[0])
|
237 |
+
node_tree.links.new(shadow_rate_node.inputs[0], shade_mate_node.outputs[3])
|
238 |
+
node_tree.links.new(invert_node.inputs[1], shadow_rate_node.outputs[0])
|
239 |
+
node_tree.links.new(shadow_mix_node.inputs[0], invert_node.outputs[0])
|
240 |
+
node_tree.links.new(toon_node.inputs[0], shade_mate_node.outputs[3])
|
241 |
+
node_tree.links.new(shadow_texture_node.inputs[0], geometry_node.outputs[4])
|
242 |
+
node_tree.links.new(shadow_mix_node.inputs[2], shadow_texture_node.outputs[1])
|
243 |
+
node_tree.links.new(toon_node.inputs[0], shade_mate_node.outputs[3])
|
244 |
+
node_tree.links.new(toon_mix_node.inputs[1], shadow_mix_node.outputs[0])
|
245 |
+
node_tree.links.new(toon_mix_node.inputs[2], toon_node.outputs[0])
|
246 |
+
node_tree.links.new(specular_mix_node.inputs[1], toon_mix_node.outputs[0])
|
247 |
+
node_tree.links.new(specular_mix_node.inputs[2], shade_mate_node.outputs[4])
|
248 |
+
node_tree.links.new(normal_node.inputs[0], mate_node.outputs[2])
|
249 |
+
node_tree.links.new(rim_ramp_node.inputs[0], normal_node.outputs[1])
|
250 |
+
node_tree.links.new(rim_power_node.inputs[4], rim_ramp_node.outputs[0])
|
251 |
+
node_tree.links.new(rim_mix_node.inputs[2], rim_power_node.outputs[0])
|
252 |
+
node_tree.links.new(rim_mix_node.inputs[0], shadow_rate_node.outputs[0])
|
253 |
+
node_tree.links.new(rim_mix_node.inputs[1], specular_mix_node.outputs[0])
|
254 |
+
node_tree.links.new(out_node.inputs[0], rim_mix_node.outputs[0])
|
255 |
+
node_tree.links.new(out_node.inputs[1], mate_node.outputs[1])
|
256 |
+
|
257 |
+
for node in node_tree.nodes[:]:
|
258 |
+
node.select = False
|
259 |
+
node_tree.nodes.active = mate_node
|
260 |
+
node_tree.nodes.active.select = True
|
261 |
+
|
262 |
+
else:
|
263 |
+
mate.use_nodes = False
|
264 |
+
mate.use_shadeless = False
|
265 |
+
|
266 |
+
# 画像のおおよその平均色を取得
|
267 |
+
def get_image_average_color(img, sample_count=10):
|
268 |
+
if not len(img.pixels): return mathutils.Color([0, 0, 0])
|
269 |
+
|
270 |
+
pixel_count = img.size[0] * img.size[1]
|
271 |
+
channels = img.channels
|
272 |
+
|
273 |
+
max_s = 0.0
|
274 |
+
max_s_color, average_color = mathutils.Color([0, 0, 0]), mathutils.Color([0, 0, 0])
|
275 |
+
seek_interval = pixel_count / sample_count
|
276 |
+
for sample_index in range(sample_count):
|
277 |
+
|
278 |
+
index = int(seek_interval * sample_index) * channels
|
279 |
+
color = mathutils.Color(img.pixels[index:index+3])
|
280 |
+
average_color += color
|
281 |
+
if max_s < color.s:
|
282 |
+
max_s_color, max_s = color, color.s
|
283 |
+
|
284 |
+
average_color /= sample_count
|
285 |
+
output_color = (average_color + max_s_color) / 2
|
286 |
+
output_color.s *= 1.5
|
287 |
+
return max_s_color
|
288 |
+
|
289 |
+
# 画像のおおよその平均色を取得 (UV版)
|
290 |
+
def get_image_average_color_uv(img, me=None, mate_index=-1, sample_count=10):
|
291 |
+
if not len(img.pixels): return mathutils.Color([0, 0, 0])
|
292 |
+
|
293 |
+
img_width, img_height, img_channel = img.size[0], img.size[1], img.channels
|
294 |
+
|
295 |
+
bm = bmesh.new()
|
296 |
+
bm.from_mesh(me)
|
297 |
+
uv_lay = bm.loops.layers.uv.active
|
298 |
+
uvs = [l[uv_lay].uv[:] for f in bm.faces if f.material_index == mate_index for l in f.loops]
|
299 |
+
bm.free()
|
300 |
+
|
301 |
+
if len(uvs) <= sample_count:
|
302 |
+
return get_image_average_color(img)
|
303 |
+
|
304 |
+
average_color = mathutils.Color([0, 0, 0])
|
305 |
+
max_s = 0.0
|
306 |
+
max_s_color = mathutils.Color([0, 0, 0])
|
307 |
+
seek_interval = len(uvs) / sample_count
|
308 |
+
for sample_index in range(sample_count):
|
309 |
+
|
310 |
+
uv_index = int(seek_interval * sample_index)
|
311 |
+
x, y = uvs[uv_index]
|
312 |
+
|
313 |
+
x = math.modf(x)[0]
|
314 |
+
if x < 0.0: x += 1.0
|
315 |
+
y = math.modf(y)[0]
|
316 |
+
if y < 0.0: y += 1.0
|
317 |
+
|
318 |
+
x, y = int(x * img_width), int(y * img_height)
|
319 |
+
|
320 |
+
pixel_index = ((y * img_width) + x) * img_channel
|
321 |
+
color = mathutils.Color(img.pixels[pixel_index:pixel_index+3])
|
322 |
+
|
323 |
+
average_color += color
|
324 |
+
if max_s < color.s:
|
325 |
+
max_s_color, max_s = color, color.s
|
326 |
+
|
327 |
+
average_color /= sample_count
|
328 |
+
output_color = (average_color + max_s_color) / 2
|
329 |
+
output_color.s *= 1.5
|
330 |
+
return output_color
|
331 |
+
|
332 |
+
# CM3D2のインストールフォルダを取得+α
|
333 |
+
def default_cm3d2_dir(base_dir, file_name, new_ext):
|
334 |
+
if not base_dir:
|
335 |
+
if preferences().cm3d2_path:
|
336 |
+
base_dir = os.path.join(preferences().cm3d2_path, "GameData", "*." + new_ext)
|
337 |
+
else:
|
338 |
+
try:
|
339 |
+
import winreg
|
340 |
+
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, r'Software\KISS\カスタムメイド3D2') as key:
|
341 |
+
base_dir = winreg.QueryValueEx(key, 'InstallPath')[0]
|
342 |
+
preferences().cm3d2_path = base_dir
|
343 |
+
base_dir = os.path.join(base_dir, "GameData", "*." + new_ext)
|
344 |
+
except: pass
|
345 |
+
if file_name:
|
346 |
+
base_dir = os.path.join(os.path.split(base_dir)[0], file_name)
|
347 |
+
base_dir = os.path.splitext(base_dir)[0] + "." + new_ext
|
348 |
+
return base_dir
|
349 |
+
|
350 |
+
# 一時ファイル書き込みと自動バックアップを行うファイルオブジェクトを返す
|
351 |
+
def open_temporary(filepath, mode, is_backup=False):
|
352 |
+
backup_ext = preferences().backup_ext
|
353 |
+
if is_backup and backup_ext:
|
354 |
+
backup_filepath = filepath + '.' + backup_ext
|
355 |
+
else:
|
356 |
+
backup_filepath = None
|
357 |
+
return fileutil.TemporaryFileWriter(filepath, mode, backup_filepath=backup_filepath)
|
358 |
+
|
359 |
+
# ファイルを上書きするならバックアップ処理
|
360 |
+
def file_backup(filepath, enable=True):
|
361 |
+
backup_ext = preferences().backup_ext
|
362 |
+
if enable and backup_ext and os.path.exists(filepath):
|
363 |
+
shutil.copyfile(filepath, filepath+"."+backup_ext)
|
364 |
+
|
365 |
+
# サブフォルダを再帰的に検索してリスト化
|
366 |
+
def fild_tex_all_files(dir):
|
367 |
+
for root, dirs, files in os.walk(dir):
|
368 |
+
yield root
|
369 |
+
for file in files:
|
370 |
+
if os.path.splitext(file)[1].lower() == ".tex":
|
371 |
+
yield os.path.join(root, file)
|
372 |
+
elif os.path.splitext(file)[1].lower() == ".png":
|
373 |
+
yield os.path.join(root, file)
|
374 |
+
|
375 |
+
# テクスチャ置き場のパスのリストを返す
|
376 |
+
def get_default_tex_paths():
|
377 |
+
default_paths = [preferences().default_tex_path0, preferences().default_tex_path1, preferences().default_tex_path2, preferences().default_tex_path3]
|
378 |
+
if not any(default_paths):
|
379 |
+
|
380 |
+
cm3d2_dir = preferences().cm3d2_path
|
381 |
+
if not cm3d2_dir:
|
382 |
+
try:
|
383 |
+
import winreg
|
384 |
+
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, r'Software\KISS\カスタムメイド3D2') as key:
|
385 |
+
cm3d2_dir = winreg.QueryValueEx(key, 'InstallPath')[0]
|
386 |
+
except: return []
|
387 |
+
|
388 |
+
target_dir = [os.path.join(cm3d2_dir, "GameData", "texture")]
|
389 |
+
target_dir.append(os.path.join(cm3d2_dir, "GameData", "texture2"))
|
390 |
+
target_dir.append(os.path.join(cm3d2_dir, "Sybaris", "GameData"))
|
391 |
+
target_dir.append(os.path.join(cm3d2_dir, "Mod"))
|
392 |
+
|
393 |
+
tex_dirs = [path for path in target_dir if os.path.isdir(path)]
|
394 |
+
|
395 |
+
for index, path in enumerate(tex_dirs):
|
396 |
+
preferences().__setattr__('default_tex_path' + str(index), path)
|
397 |
+
else:
|
398 |
+
tex_dirs = [preferences().__getattribute__('default_tex_path' + str(i)) for i in range(4) if preferences().__getattribute__('default_tex_path' + str(i))]
|
399 |
+
return tex_dirs
|
400 |
+
|
401 |
+
# テクスチャ置き場の全ファイルを返す
|
402 |
+
def get_tex_storage_files():
|
403 |
+
files = []
|
404 |
+
tex_dirs = get_default_tex_paths()
|
405 |
+
for tex_dir in tex_dirs:
|
406 |
+
tex_dir = bpy.path.abspath(tex_dir)
|
407 |
+
files.extend(fild_tex_all_files(tex_dir))
|
408 |
+
return files
|
409 |
+
|
410 |
+
# テクスチャを検索して空の画像へ置換
|
411 |
+
def replace_cm3d2_tex(img, pre_files=[]):
|
412 |
+
source_png_name = remove_serial_number(img.name).lower() + ".png"
|
413 |
+
source_tex_name = remove_serial_number(img.name).lower() + ".tex"
|
414 |
+
|
415 |
+
tex_dirs = get_default_tex_paths()
|
416 |
+
|
417 |
+
for tex_dir in tex_dirs:
|
418 |
+
|
419 |
+
if len(pre_files):
|
420 |
+
files = pre_files
|
421 |
+
else:
|
422 |
+
files = fild_tex_all_files(tex_dir)
|
423 |
+
|
424 |
+
for path in files:
|
425 |
+
path = bpy.path.abspath(path)
|
426 |
+
file_name = os.path.basename(path).lower()
|
427 |
+
|
428 |
+
if file_name == source_png_name:
|
429 |
+
img.filepath = path
|
430 |
+
img.reload()
|
431 |
+
return True
|
432 |
+
|
433 |
+
elif file_name == source_tex_name:
|
434 |
+
try:
|
435 |
+
file = open(path, 'rb')
|
436 |
+
except: return False
|
437 |
+
|
438 |
+
header_ext = read_str(file)
|
439 |
+
if header_ext == 'CM3D2_TEX':
|
440 |
+
file.seek(4, 1)
|
441 |
+
read_str(file)
|
442 |
+
png_size = struct.unpack('<i', file.read(4))[0]
|
443 |
+
png_path = os.path.splitext(path)[0] + ".png"
|
444 |
+
try:
|
445 |
+
png_file = open(png_path, 'wb')
|
446 |
+
except: return False
|
447 |
+
png_file.write(file.read(png_size))
|
448 |
+
png_file.close() ; file.close()
|
449 |
+
img.filepath = png_path
|
450 |
+
img.reload()
|
451 |
+
return True
|
452 |
+
else:
|
453 |
+
file.close()
|
454 |
+
return False
|
455 |
+
|
456 |
+
if len(pre_files):
|
457 |
+
return False
|
458 |
+
return False
|
459 |
+
|
460 |
+
# col f タイプの設定値を値に合わせて着色
|
461 |
+
def set_texture_color(slot):
|
462 |
+
if not slot or not slot.texture or slot.use: return
|
463 |
+
|
464 |
+
type = 'col' if slot.use_rgb_to_intensity else 'f'
|
465 |
+
tex = slot.texture
|
466 |
+
base_name = remove_serial_number(tex.name)
|
467 |
+
tex.type = 'BLEND'
|
468 |
+
if 'progression' in dir(tex):
|
469 |
+
tex.progression = 'DIAGONAL'
|
470 |
+
tex.use_color_ramp = True
|
471 |
+
tex.use_preview_alpha = True
|
472 |
+
elements = tex.color_ramp.elements
|
473 |
+
|
474 |
+
element_count = 4
|
475 |
+
if element_count < len(elements):
|
476 |
+
for i in range(len(elements) - element_count):
|
477 |
+
elements.remove(elements[-1])
|
478 |
+
elif len(elements) < element_count:
|
479 |
+
for i in range(element_count - len(elements)):
|
480 |
+
elements.new(1.0)
|
481 |
+
|
482 |
+
elements[0].position, elements[1].position, elements[2].position, elements[3].position = 0.2, 0.21, 0.25, 0.26
|
483 |
+
|
484 |
+
if type == 'col':
|
485 |
+
elements[0].color = [0.2, 1, 0.2, 1]
|
486 |
+
elements[-1].color = slot.color[:] + (slot.diffuse_color_factor, )
|
487 |
+
if 0.3 < mathutils.Color(slot.color[:3]).v:
|
488 |
+
elements[1].color, elements[2].color = [0, 0, 0, 1], [0, 0, 0, 1]
|
489 |
+
else:
|
490 |
+
elements[1].color, elements[2].color = [1, 1, 1, 1], [1, 1, 1, 1]
|
491 |
+
|
492 |
+
elif type == 'f':
|
493 |
+
elements[0].color = [0.2, 0.2, 1, 1]
|
494 |
+
multi = 1.0
|
495 |
+
if base_name == '_OutlineWidth':
|
496 |
+
multi = 200
|
497 |
+
elif base_name == '_RimPower':
|
498 |
+
multi = 1.0 / 30.0
|
499 |
+
value = slot.diffuse_color_factor * multi
|
500 |
+
elements[-1].color = [value, value, value, 1]
|
501 |
+
if 0.3 < value:
|
502 |
+
elements[1].color, elements[2].color = [0, 0, 0, 1], [0, 0, 0, 1]
|
503 |
+
else:
|
504 |
+
elements[1].color, elements[2].color = [1, 1, 1, 1], [1, 1, 1, 1]
|
505 |
+
|
506 |
+
# 必要なエリアタイプを設定を変更してでも取得
|
507 |
+
def get_request_area(context, request_type, except_types=['VIEW_3D', 'PROPERTIES', 'INFO', 'USER_PREFERENCES']):
|
508 |
+
request_areas = [(a, a.width * a.height) for a in context.screen.areas if a.type == request_type]
|
509 |
+
candidate_areas = [(a, a.width * a.height) for a in context.screen.areas if a.type not in except_types]
|
510 |
+
|
511 |
+
return_areas = request_areas[:] if len(request_areas) else candidate_areas
|
512 |
+
if not len(return_areas): return None
|
513 |
+
|
514 |
+
return_areas.sort(key=lambda i: i[1])
|
515 |
+
return_area = return_areas[-1][0]
|
516 |
+
return_area.type = request_type
|
517 |
+
return return_area
|
518 |
+
|
519 |
+
# 複数のデータを完全に削除
|
520 |
+
def remove_data(target_data):
|
521 |
+
try: target_data = target_data[:]
|
522 |
+
except: target_data = [target_data]
|
523 |
+
|
524 |
+
for data in target_data:
|
525 |
+
if data.__class__.__name__ == 'Object':
|
526 |
+
if data.name in bpy.context.scene.objects:
|
527 |
+
bpy.context.scene.objects.unlink(data)
|
528 |
+
|
529 |
+
for data in target_data:
|
530 |
+
if 'users' in dir(data) and 'user_clear' in dir(data):
|
531 |
+
if data.users: data.user_clear()
|
532 |
+
|
533 |
+
for data in target_data:
|
534 |
+
for data_str in dir(bpy.data):
|
535 |
+
if data_str[-1] != "s": continue
|
536 |
+
try:
|
537 |
+
if data.__class__.__name__ == eval('bpy.data.%s[0].__class__.__name__' % data_str):
|
538 |
+
exec('bpy.data.%s.remove(data, do_unlink=True)' % data_str)
|
539 |
+
break
|
540 |
+
except: pass
|
541 |
+
|
542 |
+
# オブジェクトのマテリアルを削除/復元するクラス
|
543 |
+
class material_restore:
|
544 |
+
def __init__(self, ob):
|
545 |
+
override = bpy.context.copy()
|
546 |
+
override['object'] = ob
|
547 |
+
self.object = ob
|
548 |
+
|
549 |
+
self.slots = []
|
550 |
+
for slot in ob.material_slots:
|
551 |
+
if slot: self.slots.append(slot.material)
|
552 |
+
else: self.slots.append(None)
|
553 |
+
|
554 |
+
self.mesh_data = []
|
555 |
+
for index, slot in enumerate(ob.material_slots):
|
556 |
+
self.mesh_data.append([])
|
557 |
+
for face in ob.data.polygons:
|
558 |
+
if face.material_index == index:
|
559 |
+
self.mesh_data[-1].append(face.index)
|
560 |
+
|
561 |
+
for slot in ob.material_slots[:]:
|
562 |
+
bpy.ops.object.material_slot_remove(override)
|
563 |
+
|
564 |
+
def restore(self):
|
565 |
+
override = bpy.context.copy()
|
566 |
+
override['object'] = self.object
|
567 |
+
|
568 |
+
for slot in self.object.material_slots[:]:
|
569 |
+
bpy.ops.object.material_slot_remove(override)
|
570 |
+
|
571 |
+
for index, mate in enumerate(self.slots):
|
572 |
+
bpy.ops.object.material_slot_add(override)
|
573 |
+
slot = self.object.material_slots[index]
|
574 |
+
if slot:
|
575 |
+
slot.material = mate
|
576 |
+
for face_index in self.mesh_data[index]:
|
577 |
+
self.object.data.polygons[face_index].material_index = index
|
578 |
+
|
579 |
+
# 現在のレイヤー内のオブジェクトをレンダリングしなくする/戻す
|
580 |
+
class hide_render_restore:
|
581 |
+
def __init__(self, render_objects=[]):
|
582 |
+
try: render_objects = render_objects[:]
|
583 |
+
except: render_objects = [render_objects]
|
584 |
+
|
585 |
+
if not len(render_objects):
|
586 |
+
render_objects = bpy.context.selected_objects[:]
|
587 |
+
|
588 |
+
self.render_objects = render_objects[:]
|
589 |
+
self.render_object_names = [ob.name for ob in render_objects]
|
590 |
+
|
591 |
+
self.rendered_objects = []
|
592 |
+
for ob in render_objects:
|
593 |
+
if ob.hide_render:
|
594 |
+
self.rendered_objects.append(ob)
|
595 |
+
ob.hide_render = False
|
596 |
+
|
597 |
+
self.hide_rendered_objects = []
|
598 |
+
for ob in bpy.data.objects:
|
599 |
+
for layer_index, is_used in enumerate(bpy.context.scene.layers):
|
600 |
+
if not is_used: continue
|
601 |
+
if ob.layers[layer_index] and is_used and ob.name not in self.render_object_names and not ob.hide_render:
|
602 |
+
self.hide_rendered_objects.append(ob)
|
603 |
+
ob.hide_render = True
|
604 |
+
break
|
605 |
+
|
606 |
+
def restore(self):
|
607 |
+
for ob in self.rendered_objects:
|
608 |
+
ob.hide_render = True
|
609 |
+
for ob in self.hide_rendered_objects:
|
610 |
+
ob.hide_render = False
|
611 |
+
|
612 |
+
# 指定エリアに変数をセット
|
613 |
+
def set_area_space_attr(area, attr_name, value):
|
614 |
+
if not area: return
|
615 |
+
for space in area.spaces:
|
616 |
+
if space.type == area.type:
|
617 |
+
space.__setattr__(attr_name, value)
|
618 |
+
break
|
619 |
+
|
620 |
+
# スムーズなグラフを返す1
|
621 |
+
def in_out_quad_blend(f):
|
622 |
+
if f <= 0.5:
|
623 |
+
return 2.0 * math.sqrt(f)
|
624 |
+
f -= 0.5
|
625 |
+
return 2.0 * f * (1.0 - f) + 0.5
|
626 |
+
# スムーズなグラフを返す2
|
627 |
+
def bezier_blend(f):
|
628 |
+
return math.sqrt(f) * (3.0 - 2.0 * f)
|
629 |
+
# 三角関数でスムーズなグラフを返す
|
630 |
+
def trigonometric_smooth(x):
|
631 |
+
return math.sin((x-0.5)*math.pi)*0.5+0.5
|
632 |
+
|
633 |
+
# エクスポート例外クラス
|
634 |
+
class CM3D2ExportException(Exception):
|
635 |
+
pass
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/console_toggle.py
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
import os, bpy
|
2 |
+
if os.name == 'nt':
|
3 |
+
bpy.ops.wm.console_toggle()
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/fileutil.py
ADDED
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import io
|
2 |
+
import os
|
3 |
+
import shutil
|
4 |
+
import tempfile
|
5 |
+
|
6 |
+
|
7 |
+
|
8 |
+
class TemporaryFileWriter(io.BufferedWriter):
|
9 |
+
"""ファイルをアトミックに更新します。"""
|
10 |
+
|
11 |
+
backup_filepath = None
|
12 |
+
__filepath = None
|
13 |
+
__temppath = None
|
14 |
+
|
15 |
+
|
16 |
+
@property
|
17 |
+
def filepath(self):
|
18 |
+
"""ファイルパスを取得します。"""
|
19 |
+
return self.__filepath
|
20 |
+
|
21 |
+
|
22 |
+
@property
|
23 |
+
def temppath(self):
|
24 |
+
"""一時ファイルパスを取得します。"""
|
25 |
+
return self.__temppath
|
26 |
+
|
27 |
+
|
28 |
+
def __init__(self, filepath, mode='wb', buffer_size=io.DEFAULT_BUFFER_SIZE, backup_filepath=None):
|
29 |
+
"""ファイルパスを指定して初期化します。
|
30 |
+
backup_filepath に None 以外が指定された場合、書き込み完了時に
|
31 |
+
バックアップファイルが作成されます。
|
32 |
+
"""
|
33 |
+
dirpath, filename = os.path.split(filepath)
|
34 |
+
fd, temppath = tempfile.mkstemp(prefix=filename + '.', dir=dirpath)
|
35 |
+
try:
|
36 |
+
fh = os.fdopen(fd, mode)
|
37 |
+
super(TemporaryFileWriter, self).__init__(fh, buffer_size)
|
38 |
+
except:
|
39 |
+
if fh:
|
40 |
+
fh.close()
|
41 |
+
os.remove(temppath)
|
42 |
+
raise
|
43 |
+
self.__filepath = filepath
|
44 |
+
self.__temppath = temppath
|
45 |
+
self.backup_filepath = backup_filepath
|
46 |
+
|
47 |
+
|
48 |
+
def __enter__(self):
|
49 |
+
return self
|
50 |
+
|
51 |
+
|
52 |
+
def __exit__(self, exc_type, exc_value, traceback):
|
53 |
+
if exc_type is None and exc_value is None and traceback is None:
|
54 |
+
self.close()
|
55 |
+
else:
|
56 |
+
self.abort()
|
57 |
+
|
58 |
+
|
59 |
+
def close(self):
|
60 |
+
"""一時ファイルを閉じてリネームします。"""
|
61 |
+
if self.closed:
|
62 |
+
return
|
63 |
+
super(io.BufferedWriter, self).close()
|
64 |
+
self.raw.close()
|
65 |
+
try:
|
66 |
+
if os.path.exists(self.filepath):
|
67 |
+
if self.backup_filepath is not None:
|
68 |
+
shutil.move(self.filepath, self.backup_filepath)
|
69 |
+
else:
|
70 |
+
os.remove(self.filepath)
|
71 |
+
shutil.move(self.temppath, self.filepath)
|
72 |
+
except:
|
73 |
+
os.remove(self.temppath)
|
74 |
+
raise
|
75 |
+
|
76 |
+
|
77 |
+
def abort(self):
|
78 |
+
"""一時ファイルを閉じて削除します。"""
|
79 |
+
if self.closed:
|
80 |
+
return
|
81 |
+
super(io.BufferedWriter, self).close()
|
82 |
+
self.raw.close()
|
83 |
+
os.remove(self.temppath)
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/kiss.png
ADDED
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/mate_export.py
ADDED
@@ -0,0 +1,257 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import bpy
|
2 |
+
import os
|
3 |
+
import re
|
4 |
+
import struct
|
5 |
+
from . import common
|
6 |
+
|
7 |
+
class export_cm3d2_mate(bpy.types.Operator):
|
8 |
+
bl_idname = 'material.export_cm3d2_mate'
|
9 |
+
bl_label = "Save As Mate"
|
10 |
+
bl_description = "Allows you to save blender CM3d2 materials as seperate .mate files."
|
11 |
+
bl_options = {'REGISTER', 'UNDO'}
|
12 |
+
|
13 |
+
filepath = bpy.props.StringProperty(subtype='FILE_PATH')
|
14 |
+
filename_ext = ".mate"
|
15 |
+
filter_glob = bpy.props.StringProperty(default="*.mate", options={'HIDDEN'})
|
16 |
+
|
17 |
+
is_backup = bpy.props.BoolProperty(name="Backup", default=True, description="Will backup an overwritten file.")
|
18 |
+
|
19 |
+
version = bpy.props.IntProperty(name="Version", default=1000, min=1000, max=1111, soft_min=1000, soft_max=1111, step=1)
|
20 |
+
name1 = bpy.props.StringProperty(name="Name 1")
|
21 |
+
name2 = bpy.props.StringProperty(name="Name 2")
|
22 |
+
|
23 |
+
@classmethod
|
24 |
+
def poll(cls, context):
|
25 |
+
if 'material' in dir(context):
|
26 |
+
mate = context.material
|
27 |
+
if mate:
|
28 |
+
if 'shader1' in mate and 'shader2' in mate:
|
29 |
+
return True
|
30 |
+
return False
|
31 |
+
|
32 |
+
def invoke(self, context, event):
|
33 |
+
mate = context.material
|
34 |
+
if common.preferences().mate_default_path:
|
35 |
+
self.filepath = common.default_cm3d2_dir(common.preferences().mate_default_path, mate.name.lower(), "mate")
|
36 |
+
else:
|
37 |
+
self.filepath = common.default_cm3d2_dir(common.preferences().mate_export_path, mate.name.lower(), "mate")
|
38 |
+
self.is_backup = bool(common.preferences().backup_ext)
|
39 |
+
self.name1 = common.remove_serial_number(mate.name.lower())
|
40 |
+
self.name2 = common.remove_serial_number(mate.name)
|
41 |
+
context.window_manager.fileselect_add(self)
|
42 |
+
return {'RUNNING_MODAL'}
|
43 |
+
|
44 |
+
def draw(self, context):
|
45 |
+
row = self.layout.row()
|
46 |
+
row.prop(self, 'is_backup', icon='FILE_BACKUP')
|
47 |
+
if not common.preferences().backup_ext:
|
48 |
+
row.enabled = False
|
49 |
+
self.layout.prop(self, 'version', icon='LINENUMBERS_ON')
|
50 |
+
self.layout.prop(self, 'name1', icon='SORTALPHA')
|
51 |
+
self.layout.prop(self, 'name2', icon='SORTALPHA')
|
52 |
+
|
53 |
+
def execute(self, context):
|
54 |
+
common.preferences().mate_export_path = self.filepath
|
55 |
+
|
56 |
+
try:
|
57 |
+
file = common.open_temporary(self.filepath, 'wb', is_backup=self.is_backup)
|
58 |
+
except:
|
59 |
+
self.report(type={'ERROR'}, message="Failed to backup file, possibly inaccessible.")
|
60 |
+
return {'CANCELLED'}
|
61 |
+
|
62 |
+
try:
|
63 |
+
with file:
|
64 |
+
self.write_material(context, file)
|
65 |
+
except common.CM3D2ExportException as e:
|
66 |
+
self.report(type={'ERROR'}, message=str(e))
|
67 |
+
return {'CANCELLED'}
|
68 |
+
|
69 |
+
return {'FINISHED'}
|
70 |
+
|
71 |
+
def write_material(self, context, file):
|
72 |
+
mate = context.material
|
73 |
+
|
74 |
+
common.write_str(file, 'CM3D2_MATERIAL')
|
75 |
+
file.write(struct.pack('<i', self.version))
|
76 |
+
|
77 |
+
common.write_str(file, self.name1)
|
78 |
+
common.write_str(file, self.name2)
|
79 |
+
common.write_str(file, mate['shader1'])
|
80 |
+
common.write_str(file, mate['shader2'])
|
81 |
+
|
82 |
+
for tex_slot in mate.texture_slots:
|
83 |
+
if not tex_slot:
|
84 |
+
continue
|
85 |
+
tex = tex_slot.texture
|
86 |
+
if tex_slot.use:
|
87 |
+
type = 'tex'
|
88 |
+
else:
|
89 |
+
if tex_slot.use_rgb_to_intensity:
|
90 |
+
type = 'col'
|
91 |
+
else:
|
92 |
+
type = 'f'
|
93 |
+
common.write_str(file, type)
|
94 |
+
common.write_str(file, common.remove_serial_number(tex.name))
|
95 |
+
if type == 'tex':
|
96 |
+
try:
|
97 |
+
img = tex.image
|
98 |
+
except:
|
99 |
+
raise common.CM3D2ExportException("Couldn't acquire tex type, mission abort.")
|
100 |
+
if img:
|
101 |
+
common.write_str(file, 'tex2d')
|
102 |
+
common.write_str(file, common.remove_serial_number(img.name))
|
103 |
+
if 'cm3d2_path' in img:
|
104 |
+
path = img['cm3d2_path']
|
105 |
+
else:
|
106 |
+
path = bpy.path.abspath(img.filepath)
|
107 |
+
path = path.replace('\\', '/')
|
108 |
+
path = re.sub(r'^[\/\.]*', "", path)
|
109 |
+
if not re.search(r'^assets/texture/', path, re.I):
|
110 |
+
path = "Assets/texture/texture/" + os.path.basename(path)
|
111 |
+
common.write_str(file, path)
|
112 |
+
col = tex_slot.color
|
113 |
+
file.write(struct.pack('<3f', col[0], col[1], col[2]))
|
114 |
+
file.write(struct.pack('<f', tex_slot.diffuse_color_factor))
|
115 |
+
else:
|
116 |
+
common.write_str(file, 'null')
|
117 |
+
elif type == 'col':
|
118 |
+
col = tex_slot.color
|
119 |
+
file.write(struct.pack('<3f', col[0], col[1], col[2]))
|
120 |
+
file.write(struct.pack('<f', tex_slot.diffuse_color_factor))
|
121 |
+
elif type == 'f':
|
122 |
+
file.write(struct.pack('<f', tex_slot.diffuse_color_factor))
|
123 |
+
|
124 |
+
common.write_str(file, 'end')
|
125 |
+
|
126 |
+
class export_cm3d2_mate_text(bpy.types.Operator):
|
127 |
+
bl_idname = 'text.export_cm3d2_mate_text'
|
128 |
+
bl_label = "Save Text as Mate"
|
129 |
+
bl_description = "This will allow you to save any text in the text editor as a .mate file"
|
130 |
+
bl_options = {'REGISTER', 'UNDO'}
|
131 |
+
|
132 |
+
filepath = bpy.props.StringProperty(subtype='FILE_PATH')
|
133 |
+
filename_ext = ".mate"
|
134 |
+
filter_glob = bpy.props.StringProperty(default="*.mate", options={'HIDDEN'})
|
135 |
+
|
136 |
+
is_backup = bpy.props.BoolProperty(name="Backup", default=True, description="Will backup any overwritten files.")
|
137 |
+
|
138 |
+
version = bpy.props.IntProperty(name="Version", default=1000, min=1000, max=1111, soft_min=1000, soft_max=1111, step=1)
|
139 |
+
name1 = bpy.props.StringProperty(name="Name1")
|
140 |
+
name2 = bpy.props.StringProperty(name="Name2")
|
141 |
+
|
142 |
+
@classmethod
|
143 |
+
def poll(cls, context):
|
144 |
+
if 'edit_text' in dir(context):
|
145 |
+
txt = context.edit_text
|
146 |
+
if txt:
|
147 |
+
data = txt.as_string()
|
148 |
+
lines = txt.as_string().split('\n')
|
149 |
+
if len(lines) < 10:
|
150 |
+
return False
|
151 |
+
match_strs = ['\ntex\n', '\ncol\n', '\nf\n', '\n\t_MainTex\n', '\n\t_Color\n', '\n\t_Shininess\n']
|
152 |
+
for s in match_strs:
|
153 |
+
if s in data:
|
154 |
+
return True
|
155 |
+
return False
|
156 |
+
|
157 |
+
def invoke(self, context, event):
|
158 |
+
txt = context.edit_text
|
159 |
+
lines = txt.as_string().split('\n')
|
160 |
+
mate_name = lines[1]
|
161 |
+
if common.preferences().mate_default_path:
|
162 |
+
self.filepath = common.default_cm3d2_dir(common.preferences().mate_default_path, mate_name.lower(), "mate")
|
163 |
+
else:
|
164 |
+
self.filepath = common.default_cm3d2_dir(common.preferences().mate_export_path, mate_name.lower(), "mate")
|
165 |
+
try:
|
166 |
+
self.version = int(lines[0])
|
167 |
+
except:
|
168 |
+
self.version = 1000
|
169 |
+
if lines[1] != '***':
|
170 |
+
self.name1 = lines[1]
|
171 |
+
else:
|
172 |
+
self.name1 = lines[2]
|
173 |
+
self.name2 = lines[2]
|
174 |
+
context.window_manager.fileselect_add(self)
|
175 |
+
return {'RUNNING_MODAL'}
|
176 |
+
|
177 |
+
def draw(self, context):
|
178 |
+
row = self.layout.row()
|
179 |
+
row.prop(self, 'is_backup', icon='FILE_BACKUP')
|
180 |
+
if not common.preferences().backup_ext:
|
181 |
+
row.enabled = False
|
182 |
+
self.layout.prop(self, 'version', icon='LINENUMBERS_ON')
|
183 |
+
self.layout.prop(self, 'name1', icon='SORTALPHA')
|
184 |
+
self.layout.prop(self, 'name2', icon='SORTALPHA')
|
185 |
+
|
186 |
+
def execute(self, context):
|
187 |
+
common.preferences().mate_export_path = self.filepath
|
188 |
+
|
189 |
+
try:
|
190 |
+
file = common.open_temporary(self.filepath, 'wb', is_backup=self.is_backup)
|
191 |
+
except:
|
192 |
+
self.report(type={'ERROR'}, message="Failed to backup file, possibly inaccessible.")
|
193 |
+
return {'CANCELLED'}
|
194 |
+
|
195 |
+
try:
|
196 |
+
with file:
|
197 |
+
self.write_material(context, file)
|
198 |
+
except common.CM3D2ExportException as e:
|
199 |
+
self.report(type={'ERROR'}, message=str(e))
|
200 |
+
return {'CANCELLED'}
|
201 |
+
|
202 |
+
return {'FINISHED'}
|
203 |
+
|
204 |
+
def write_material(self, context, file):
|
205 |
+
txt = context.edit_text
|
206 |
+
lines = txt.as_string().split('\n')
|
207 |
+
|
208 |
+
common.write_str(file, 'CM3D2_MATERIAL')
|
209 |
+
file.write(struct.pack('<i', self.version))
|
210 |
+
|
211 |
+
common.write_str(file, self.name1)
|
212 |
+
common.write_str(file, self.name2)
|
213 |
+
common.write_str(file, lines[3])
|
214 |
+
common.write_str(file, lines[4])
|
215 |
+
|
216 |
+
line_seek = 5
|
217 |
+
try:
|
218 |
+
for i in range(99999):
|
219 |
+
if len(lines) <= line_seek:
|
220 |
+
break
|
221 |
+
if not lines[line_seek]:
|
222 |
+
line_seek += 1
|
223 |
+
continue
|
224 |
+
if lines[line_seek] == 'tex':
|
225 |
+
common.write_str(file, common.line_trim(lines[line_seek]))
|
226 |
+
common.write_str(file, common.line_trim(lines[line_seek + 1]))
|
227 |
+
common.write_str(file, common.line_trim(lines[line_seek + 2]))
|
228 |
+
line_seek += 3
|
229 |
+
if common.line_trim(lines[line_seek - 1]) == 'tex2d':
|
230 |
+
common.write_str(file, common.line_trim(lines[line_seek]))
|
231 |
+
common.write_str(file, common.line_trim(lines[line_seek + 1]))
|
232 |
+
floats = common.line_trim(lines[line_seek + 2]).split(' ')
|
233 |
+
for f in floats:
|
234 |
+
file.write(struct.pack('<f', float(f)))
|
235 |
+
line_seek += 3
|
236 |
+
elif lines[line_seek] == 'col':
|
237 |
+
common.write_str(file, common.line_trim(lines[line_seek]))
|
238 |
+
common.write_str(file, common.line_trim(lines[line_seek + 1]))
|
239 |
+
floats = common.line_trim(lines[line_seek + 2]).split(' ')
|
240 |
+
for f in floats:
|
241 |
+
file.write(struct.pack('<f', float(f)))
|
242 |
+
line_seek += 3
|
243 |
+
elif lines[line_seek] == 'f':
|
244 |
+
common.write_str(file, common.line_trim(lines[line_seek]))
|
245 |
+
common.write_str(file, common.line_trim(lines[line_seek + 1]))
|
246 |
+
f = float(common.line_trim(lines[line_seek + 2]))
|
247 |
+
file.write(struct.pack('<f', f))
|
248 |
+
line_seek += 3
|
249 |
+
else:
|
250 |
+
raise common.CM3D2ExportException("Setting value other than [tex, col, or f] was found. Mission abort")
|
251 |
+
except:
|
252 |
+
raise common.CM3D2ExportException("Failed to save .mate, review data.")
|
253 |
+
common.write_str(file, 'end')
|
254 |
+
|
255 |
+
# テキストメニューに項目を登録
|
256 |
+
def TEXT_MT_text(self, context):
|
257 |
+
self.layout.operator(export_cm3d2_mate_text.bl_idname, icon_value=common.preview_collections['main']['KISS'].icon_id)
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/mate_import.py
ADDED
@@ -0,0 +1,224 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os, re, bpy, struct, os.path, shutil
|
2 |
+
from . import common
|
3 |
+
|
4 |
+
class import_cm3d2_mate(bpy.types.Operator):
|
5 |
+
bl_idname = 'material.import_cm3d2_mate'
|
6 |
+
bl_label = "Import Mate"
|
7 |
+
bl_description = "Open a .mate file as a material."
|
8 |
+
bl_options = {'REGISTER', 'UNDO'}
|
9 |
+
|
10 |
+
filepath = bpy.props.StringProperty(subtype='FILE_PATH')
|
11 |
+
filename_ext = ".mate"
|
12 |
+
filter_glob = bpy.props.StringProperty(default="*.mate", options={'HIDDEN'})
|
13 |
+
|
14 |
+
is_decorate = bpy.props.BoolProperty(name="Decorate the material according to its type", default=True)
|
15 |
+
is_replace_cm3d2_tex = bpy.props.BoolProperty(name="Find textures", default=True, description="Will search for the textures.")
|
16 |
+
|
17 |
+
@classmethod
|
18 |
+
def poll(cls, context):
|
19 |
+
if 'material_slot' in dir(context):
|
20 |
+
if 'material' in dir(context):
|
21 |
+
return True
|
22 |
+
return False
|
23 |
+
|
24 |
+
def invoke(self, context, event):
|
25 |
+
if common.preferences().mate_default_path:
|
26 |
+
self.filepath = common.default_cm3d2_dir(common.preferences().mate_default_path, "", "mate")
|
27 |
+
else:
|
28 |
+
self.filepath = common.default_cm3d2_dir(common.preferences().mate_import_path, "", "mate")
|
29 |
+
self.is_replace_cm3d2_tex = common.preferences().is_replace_cm3d2_tex
|
30 |
+
context.window_manager.fileselect_add(self)
|
31 |
+
return {'RUNNING_MODAL'}
|
32 |
+
|
33 |
+
def draw(self, context):
|
34 |
+
self.layout.prop(self, 'is_decorate', icon='TEXTURE_SHADED')
|
35 |
+
self.layout.prop(self, 'is_replace_cm3d2_tex', icon='BORDERMOVE')
|
36 |
+
|
37 |
+
def execute(self, context):
|
38 |
+
common.preferences().mate_import_path = self.filepath
|
39 |
+
|
40 |
+
ob = context.active_object
|
41 |
+
me = ob.data
|
42 |
+
|
43 |
+
try:
|
44 |
+
file = open(self.filepath, 'rb')
|
45 |
+
except:
|
46 |
+
self.report(type={'ERROR'}, message="Failed to import file, File is possibly inaccessible or non-existent.")
|
47 |
+
return {'CANCELLED'}
|
48 |
+
if common.read_str(file) != 'CM3D2_MATERIAL':
|
49 |
+
self.report(type={'ERROR'}, message="This is not a .mate file for CM3D2")
|
50 |
+
return {'CANCELLED'}
|
51 |
+
struct.unpack('<i', file.read(4))[0]
|
52 |
+
common.read_str(file)
|
53 |
+
mate_name = common.read_str(file)
|
54 |
+
|
55 |
+
if not context.material_slot:
|
56 |
+
bpy.ops.object.material_slot_add()
|
57 |
+
root, ext = os.path.splitext(os.path.basename(self.filepath))
|
58 |
+
mate = context.blend_data.materials.new(mate_name)
|
59 |
+
context.material_slot.material = mate
|
60 |
+
|
61 |
+
mate['shader1'] = common.read_str(file)
|
62 |
+
mate['shader2'] = common.read_str(file)
|
63 |
+
|
64 |
+
slot_index = 0
|
65 |
+
already_texs = []
|
66 |
+
for i in range(99999):
|
67 |
+
type = common.read_str(file)
|
68 |
+
if type == 'tex':
|
69 |
+
slot = mate.texture_slots.create(slot_index)
|
70 |
+
tex_name = common.read_str(file)
|
71 |
+
tex = context.blend_data.textures.new(tex_name, 'IMAGE')
|
72 |
+
slot.texture = tex
|
73 |
+
sub_type = common.read_str(file)
|
74 |
+
if sub_type == 'tex2d':
|
75 |
+
img = context.blend_data.images.new(common.read_str(file), 128, 128)
|
76 |
+
img['cm3d2_path'] = common.read_str(file)
|
77 |
+
img.filepath = img['cm3d2_path']
|
78 |
+
img.source = 'FILE'
|
79 |
+
tex.image = img
|
80 |
+
slot.color = struct.unpack('<3f', file.read(4*3))
|
81 |
+
slot.diffuse_color_factor = struct.unpack('<f', file.read(4))[0]
|
82 |
+
|
83 |
+
# tex探し
|
84 |
+
if self.is_replace_cm3d2_tex:
|
85 |
+
if common.replace_cm3d2_tex(img) and tex_name=='_MainTex':
|
86 |
+
for face in me.polygons:
|
87 |
+
if face.material_index == ob.active_material_index:
|
88 |
+
me.uv_textures.active.data[face.index].image = img
|
89 |
+
|
90 |
+
elif type == 'col':
|
91 |
+
slot = mate.texture_slots.create(slot_index)
|
92 |
+
tex_name = common.read_str(file)
|
93 |
+
tex = context.blend_data.textures.new(tex_name, 'BLEND')
|
94 |
+
mate.use_textures[slot_index] = False
|
95 |
+
slot.use_rgb_to_intensity = True
|
96 |
+
slot.color = struct.unpack('<3f', file.read(4*3))
|
97 |
+
slot.diffuse_color_factor = struct.unpack('<f', file.read(4))[0]
|
98 |
+
slot.texture = tex
|
99 |
+
|
100 |
+
elif type == 'f':
|
101 |
+
slot = mate.texture_slots.create(slot_index)
|
102 |
+
tex_name = common.read_str(file)
|
103 |
+
tex = context.blend_data.textures.new(tex_name, 'BLEND')
|
104 |
+
mate.use_textures[slot_index] = False
|
105 |
+
slot.diffuse_color_factor = struct.unpack('<f', file.read(4))[0]
|
106 |
+
slot.texture = tex
|
107 |
+
|
108 |
+
elif type == 'end':
|
109 |
+
break
|
110 |
+
else:
|
111 |
+
self.report(type={'ERROR'}, message="Unknown value was found, mission failed")
|
112 |
+
return {'CANCELLED'}
|
113 |
+
|
114 |
+
if common.preferences().mate_unread_same_value:
|
115 |
+
if tex_name in already_texs:
|
116 |
+
mate.texture_slots.clear(slot_index)
|
117 |
+
slot_index -= 1
|
118 |
+
already_texs.append(tex_name)
|
119 |
+
|
120 |
+
slot_index += 1
|
121 |
+
|
122 |
+
file.close()
|
123 |
+
common.decorate_material(mate, self.is_decorate, me, ob.active_material_index)
|
124 |
+
return {'FINISHED'}
|
125 |
+
|
126 |
+
class import_cm3d2_mate_text(bpy.types.Operator):
|
127 |
+
bl_idname = 'text.import_cm3d2_mate_text'
|
128 |
+
bl_label = "Import a mate"
|
129 |
+
bl_description = "Open a mate file in the text editor as text"
|
130 |
+
bl_options = {'REGISTER', 'UNDO'}
|
131 |
+
|
132 |
+
filepath = bpy.props.StringProperty(subtype='FILE_PATH')
|
133 |
+
filename_ext = ".mate"
|
134 |
+
filter_glob = bpy.props.StringProperty(default="*.mate", options={'HIDDEN'})
|
135 |
+
|
136 |
+
is_overwrite = bpy.props.BoolProperty(name="Overwrites current text in the text editor.", default=False)
|
137 |
+
|
138 |
+
@classmethod
|
139 |
+
def poll(cls, context):
|
140 |
+
return True
|
141 |
+
|
142 |
+
def invoke(self, context, event):
|
143 |
+
if common.preferences().mate_default_path:
|
144 |
+
self.filepath = common.default_cm3d2_dir(common.preferences().mate_default_path, "", "mate")
|
145 |
+
else:
|
146 |
+
self.filepath = common.default_cm3d2_dir(common.preferences().mate_import_path, "", "mate")
|
147 |
+
context.window_manager.fileselect_add(self)
|
148 |
+
return {'RUNNING_MODAL'}
|
149 |
+
|
150 |
+
def draw(self, context):
|
151 |
+
self.layout.prop(self, 'is_overwrite', icon='SAVE_COPY')
|
152 |
+
|
153 |
+
def execute(self, context):
|
154 |
+
common.preferences().mate_import_path = self.filepath
|
155 |
+
|
156 |
+
txt = None
|
157 |
+
if self.is_overwrite:
|
158 |
+
if 'edit_text' not in dir(context):
|
159 |
+
self.report(type={'ERROR'}, message="Text data could not be overwritten")
|
160 |
+
return {'CANCELLED'}
|
161 |
+
if not context.edit_text:
|
162 |
+
self.report(type={'ERROR'}, message="Text data could not be overwritten")
|
163 |
+
return {'CANCELLED'}
|
164 |
+
txt = context.edit_text
|
165 |
+
txt.clear()
|
166 |
+
|
167 |
+
try:
|
168 |
+
file = open(self.filepath, 'rb')
|
169 |
+
except:
|
170 |
+
self.report(type={'ERROR'}, message="Failed to open the file, File does not exist or is not accessible")
|
171 |
+
return {'CANCELLED'}
|
172 |
+
if common.read_str(file) != 'CM3D2_MATERIAL':
|
173 |
+
self.report(type={'ERROR'}, message="This is not a CM3D2 Mate file")
|
174 |
+
return {'CANCELLED'}
|
175 |
+
|
176 |
+
version = str(struct.unpack('<i', file.read(4))[0])
|
177 |
+
name1 = common.read_str(file)
|
178 |
+
name2 = common.read_str(file)
|
179 |
+
if not txt:
|
180 |
+
txt = context.blend_data.texts.new(os.path.basename(name2))
|
181 |
+
context.area.type = 'TEXT_EDITOR'
|
182 |
+
context.space_data.text = txt
|
183 |
+
txt.write( version + "\n" )
|
184 |
+
txt.write( name1 + "\n" )
|
185 |
+
txt.write( name2 + "\n" )
|
186 |
+
txt.write( common.read_str(file) + "\n" )
|
187 |
+
txt.write( common.read_str(file) + "\n" )
|
188 |
+
txt.write("\n")
|
189 |
+
|
190 |
+
for i in range(99999):
|
191 |
+
type = common.read_str(file)
|
192 |
+
if type == 'tex':
|
193 |
+
txt.write( type + "\n" )
|
194 |
+
txt.write( "\t" + common.read_str(file) + "\n" )
|
195 |
+
tex_type = common.read_str(file)
|
196 |
+
txt.write( "\t" + tex_type + "\n" )
|
197 |
+
if tex_type == 'tex2d':
|
198 |
+
txt.write( "\t" + common.read_str(file) + "\n" )
|
199 |
+
txt.write( "\t" + common.read_str(file) + "\n" )
|
200 |
+
fs = struct.unpack('<4f', file.read(4*4))
|
201 |
+
txt.write( "\t" + " ".join([str(fs[0]), str(fs[1]), str(fs[2]), str(fs[3])]) + "\n" )
|
202 |
+
elif type == 'col':
|
203 |
+
txt.write( type + "\n" )
|
204 |
+
txt.write( "\t" + common.read_str(file) + "\n" )
|
205 |
+
fs = struct.unpack('<4f', file.read(4*4))
|
206 |
+
txt.write( "\t" + " ".join([str(fs[0]), str(fs[1]), str(fs[2]), str(fs[3])]) + "\n" )
|
207 |
+
elif type == 'f':
|
208 |
+
txt.write( type + "\n" )
|
209 |
+
txt.write( "\t" + common.read_str(file) + "\n" )
|
210 |
+
txt.write( "\t" + str(struct.unpack('<f', file.read(4))[0]) + "\n" )
|
211 |
+
elif type == 'end':
|
212 |
+
break
|
213 |
+
else:
|
214 |
+
self.report(type={'ERROR'}, message="Unknown value was found, mission failed.")
|
215 |
+
return {'CANCELLED'}
|
216 |
+
|
217 |
+
file.close()
|
218 |
+
txt.current_line_index = 0
|
219 |
+
return {'FINISHED'}
|
220 |
+
|
221 |
+
# テキストメニューに項目を登録
|
222 |
+
def TEXT_MT_text(self, context):
|
223 |
+
self.layout.separator()
|
224 |
+
self.layout.operator(import_cm3d2_mate_text.bl_idname, icon_value=common.preview_collections['main']['KISS'].icon_id)
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_DATA_PT_context_arm.py
ADDED
@@ -0,0 +1,300 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 「プロパティ」エリア → 「アーマチュアデータ」タブ
|
2 |
+
import os, re, sys, bpy, time, bmesh, mathutils
|
3 |
+
from . import common
|
4 |
+
|
5 |
+
# メニュー等に項目追加
|
6 |
+
def menu_func(self, context):
|
7 |
+
import re
|
8 |
+
ob = context.active_object
|
9 |
+
if not ob: return
|
10 |
+
if ob.type != 'ARMATURE': return
|
11 |
+
|
12 |
+
arm = ob.data
|
13 |
+
is_boxed = False
|
14 |
+
|
15 |
+
bone_data_count = 0
|
16 |
+
if 'BoneData:0' in arm and 'LocalBoneData:0' in arm:
|
17 |
+
for key in arm.keys():
|
18 |
+
if re.search(r'^(Local)?BoneData:\d+$', key):
|
19 |
+
bone_data_count += 1
|
20 |
+
enabled_clipboard = False
|
21 |
+
clipboard = context.window_manager.clipboard
|
22 |
+
if 'BoneData:' in clipboard and 'LocalBoneData:' in clipboard:
|
23 |
+
enabled_clipboard = True
|
24 |
+
if bone_data_count or enabled_clipboard:
|
25 |
+
if not is_boxed:
|
26 |
+
box = self.layout.box()
|
27 |
+
box.label(text="For CM3D2", icon_value=common.preview_collections['main']['KISS'].icon_id)
|
28 |
+
is_boxed = True
|
29 |
+
|
30 |
+
col = box.column(align=True)
|
31 |
+
row = col.row(align=True)
|
32 |
+
row.label(text="Bone Data", icon='CONSTRAINT_BONE')
|
33 |
+
sub_row = row.row()
|
34 |
+
sub_row.alignment = 'RIGHT'
|
35 |
+
if bone_data_count:
|
36 |
+
sub_row.label(text=str(bone_data_count), icon='CHECKBOX_HLT')
|
37 |
+
else:
|
38 |
+
sub_row.label(text="0", icon='CHECKBOX_DEHLT')
|
39 |
+
row = col.row(align=True)
|
40 |
+
row.operator('object.copy_armature_bone_data_property', icon='COPYDOWN', text="Copy")
|
41 |
+
row.operator('object.paste_armature_bone_data_property', icon='PASTEDOWN', text="Paste")
|
42 |
+
row.operator('object.remove_armature_bone_data_property', icon='X', text="")
|
43 |
+
|
44 |
+
flag = False
|
45 |
+
for bone in arm.bones:
|
46 |
+
if not flag and re.search(r'[_ ]([rRlL])[_ ]', bone.name):
|
47 |
+
flag = True
|
48 |
+
if not flag and bone.name.count('*') == 1:
|
49 |
+
if re.search(r'\.([rRlL])$', bone.name):
|
50 |
+
flag = True
|
51 |
+
if flag:
|
52 |
+
if not is_boxed:
|
53 |
+
box = self.layout.box()
|
54 |
+
box.label(text="For CM3D2", icon_value=common.preview_collections['main']['KISS'].icon_id)
|
55 |
+
is_boxed = True
|
56 |
+
|
57 |
+
col = box.column(align=True)
|
58 |
+
col.label(text="Convert Bone Names", icon='SORTALPHA')
|
59 |
+
row = col.row(align=True)
|
60 |
+
row.operator('armature.decode_cm3d2_bone_names', text="CM3D2 → Blender", icon='BLENDER')
|
61 |
+
row.operator('armature.encode_cm3d2_bone_names', text="Blender → CM3D2", icon_value=common.preview_collections['main']['KISS'].icon_id)
|
62 |
+
break
|
63 |
+
|
64 |
+
if 'is T Stance' in arm:
|
65 |
+
if not is_boxed:
|
66 |
+
box = self.layout.box()
|
67 |
+
box.label(text="For CM3D2", icon_value=common.preview_collections['main']['KISS'].icon_id)
|
68 |
+
is_boxed = True
|
69 |
+
|
70 |
+
col = box.column(align=True)
|
71 |
+
col.label(text="Pause", icon='POSE_HLT')
|
72 |
+
row = col.row(align=True)
|
73 |
+
|
74 |
+
sub_row = row.row(align=True)
|
75 |
+
op = sub_row.operator('wm.context_set_int', icon='ARMATURE_DATA', text="Original")
|
76 |
+
op.data_path, op.value = 'scene.frame_current', 1
|
77 |
+
if context.scene.frame_current % 2:
|
78 |
+
sub_row.enabled = False
|
79 |
+
|
80 |
+
sub_row = row.row(align=True)
|
81 |
+
op = sub_row.operator('wm.context_set_int', icon='POSE_DATA', text="Pose data")
|
82 |
+
op.data_path, op.value = 'scene.frame_current', 0
|
83 |
+
if not context.scene.frame_current % 2:
|
84 |
+
sub_row.enabled = False
|
85 |
+
|
86 |
+
class decode_cm3d2_bone_names(bpy.types.Operator):
|
87 |
+
bl_idname = 'armature.decode_cm3d2_bone_names'
|
88 |
+
bl_label = " Decode CM3D2 bone names→Blender bones names"
|
89 |
+
bl_description = "Bone names are converted to Blender bone names for mirror functions."
|
90 |
+
bl_options = {'REGISTER', 'UNDO'}
|
91 |
+
|
92 |
+
@classmethod
|
93 |
+
def poll(cls, context):
|
94 |
+
import re
|
95 |
+
ob = context.active_object
|
96 |
+
if ob:
|
97 |
+
if ob.type == 'ARMATURE':
|
98 |
+
arm = ob.data
|
99 |
+
for bone in arm.bones:
|
100 |
+
if re.search(r'[_ ]([rRlL])[_ ]', bone.name):
|
101 |
+
return True
|
102 |
+
return False
|
103 |
+
|
104 |
+
def execute(self, context):
|
105 |
+
ob = context.active_object
|
106 |
+
arm = ob.data
|
107 |
+
convert_count = 0
|
108 |
+
for bone in arm.bones:
|
109 |
+
bone_name = common.decode_bone_name(bone.name)
|
110 |
+
if bone_name != bone.name:
|
111 |
+
bone.name = bone_name
|
112 |
+
convert_count += 1
|
113 |
+
if convert_count == 0:
|
114 |
+
self.report(type={'WARNING'}, message="No convertible names were found. Aborting.")
|
115 |
+
else:
|
116 |
+
self.report(type={'INFO'}, message="Bones names were converted for Blender. Mission Accomplished.")
|
117 |
+
return {'FINISHED'}
|
118 |
+
|
119 |
+
class encode_cm3d2_bone_names(bpy.types.Operator):
|
120 |
+
bl_idname = 'armature.encode_cm3d2_bone_names'
|
121 |
+
bl_label = "Blender bone names→CM3D2 bone names"
|
122 |
+
bl_description = "blender bone names are reverted back to CM3D2 bone names."
|
123 |
+
bl_options = {'REGISTER', 'UNDO'}
|
124 |
+
|
125 |
+
@classmethod
|
126 |
+
def poll(cls, context):
|
127 |
+
import re
|
128 |
+
ob = context.active_object
|
129 |
+
if ob:
|
130 |
+
if ob.type == 'ARMATURE':
|
131 |
+
arm = ob.data
|
132 |
+
for bone in arm.bones:
|
133 |
+
if bone.name.count('*') == 1 and re.search(r'\.([rRlL])$', bone.name):
|
134 |
+
return True
|
135 |
+
return False
|
136 |
+
|
137 |
+
def execute(self, context):
|
138 |
+
ob = context.active_object
|
139 |
+
arm = ob.data
|
140 |
+
convert_count = 0
|
141 |
+
for bone in arm.bones:
|
142 |
+
bone_name = common.encode_bone_name(bone.name)
|
143 |
+
if bone_name != bone.name:
|
144 |
+
bone.name = bone_name
|
145 |
+
convert_count += 1
|
146 |
+
if convert_count == 0:
|
147 |
+
self.report(type={'WARNING'}, message="A name that cannot be converted was found, Mission failed")
|
148 |
+
else:
|
149 |
+
self.report(type={'INFO'}, message="Bone names were converted back to CM3D2 Format. Mission Accomplished.")
|
150 |
+
return {'FINISHED'}
|
151 |
+
|
152 |
+
class copy_armature_bone_data_property(bpy.types.Operator):
|
153 |
+
bl_idname = 'object.copy_armature_bone_data_property'
|
154 |
+
bl_label = "Copy the bone Data"
|
155 |
+
bl_description = "Copy the bone Data in the armature custom properties to the clipboard."
|
156 |
+
bl_options = {'REGISTER', 'UNDO'}
|
157 |
+
|
158 |
+
@classmethod
|
159 |
+
def poll(cls, context):
|
160 |
+
ob = context.active_object
|
161 |
+
if ob:
|
162 |
+
if ob.type == 'ARMATURE':
|
163 |
+
arm = ob.data
|
164 |
+
if 'BoneData:0' in arm and 'LocalBoneData:0' in arm:
|
165 |
+
return True
|
166 |
+
return False
|
167 |
+
|
168 |
+
def execute(self, context):
|
169 |
+
output_text = ""
|
170 |
+
ob = context.active_object.data
|
171 |
+
pass_count = 0
|
172 |
+
if 'BaseBone' in ob:
|
173 |
+
output_text += "BaseBone:" + ob['BaseBone'] + "\n"
|
174 |
+
for i in range(99999):
|
175 |
+
name = "BoneData:" + str(i)
|
176 |
+
if name in ob:
|
177 |
+
output_text += "BoneData:" + ob[name] + "\n"
|
178 |
+
else:
|
179 |
+
pass_count += 1
|
180 |
+
if 10 < pass_count:
|
181 |
+
break
|
182 |
+
pass_count = 0
|
183 |
+
for i in range(99999):
|
184 |
+
name = "LocalBoneData:" + str(i)
|
185 |
+
if name in ob:
|
186 |
+
output_text += "LocalBoneData:" + ob[name] + "\n"
|
187 |
+
else:
|
188 |
+
pass_count += 1
|
189 |
+
if 10 < pass_count:
|
190 |
+
break
|
191 |
+
context.window_manager.clipboard = output_text
|
192 |
+
self.report(type={'INFO'}, message="Bone Data was copied, mission accomplished.")
|
193 |
+
return {'FINISHED'}
|
194 |
+
|
195 |
+
class paste_armature_bone_data_property(bpy.types.Operator):
|
196 |
+
bl_idname = 'object.paste_armature_bone_data_property'
|
197 |
+
bl_label = "Paste Bone Data"
|
198 |
+
bl_description = "Bone Data is pasted into the Armature custom properties. NOTE: this wil replace any Data in the custom properties."
|
199 |
+
bl_options = {'REGISTER', 'UNDO'}
|
200 |
+
|
201 |
+
@classmethod
|
202 |
+
def poll(cls, context):
|
203 |
+
ob = context.active_object
|
204 |
+
if ob:
|
205 |
+
if ob.type == 'ARMATURE':
|
206 |
+
clipboard = context.window_manager.clipboard
|
207 |
+
if 'BoneData:' in clipboard and 'LocalBoneData:' in clipboard:
|
208 |
+
return True
|
209 |
+
return False
|
210 |
+
|
211 |
+
def execute(self, context):
|
212 |
+
import re
|
213 |
+
ob = context.active_object.data
|
214 |
+
pass_count = 0
|
215 |
+
for i in range(99999):
|
216 |
+
name = "BoneData:" + str(i)
|
217 |
+
if name in ob:
|
218 |
+
del ob[name]
|
219 |
+
else:
|
220 |
+
pass_count += 1
|
221 |
+
if 10 < pass_count:
|
222 |
+
break
|
223 |
+
pass_count = 0
|
224 |
+
for i in range(99999):
|
225 |
+
name = "LocalBoneData:" + str(i)
|
226 |
+
if name in ob:
|
227 |
+
del ob[name]
|
228 |
+
else:
|
229 |
+
pass_count += 1
|
230 |
+
if 10 < pass_count:
|
231 |
+
break
|
232 |
+
bone_data_count = 0
|
233 |
+
local_bone_data_count = 0
|
234 |
+
for line in context.window_manager.clipboard.split("\n"):
|
235 |
+
r = re.search('^BaseBone:(.+)$', line)
|
236 |
+
if r:
|
237 |
+
ob['BaseBone'] = r.groups()[0]
|
238 |
+
r = re.search('^BoneData:(.+)$', line)
|
239 |
+
if r:
|
240 |
+
if line.count(',') == 4:
|
241 |
+
info = r.groups()[0]
|
242 |
+
name = "BoneData:" + str(bone_data_count)
|
243 |
+
ob[name] = info
|
244 |
+
bone_data_count += 1
|
245 |
+
r = re.search('^LocalBoneData:(.+)$', line)
|
246 |
+
if r:
|
247 |
+
if line.count(',') == 1:
|
248 |
+
info = r.groups()[0]
|
249 |
+
name = "LocalBoneData:" + str(local_bone_data_count)
|
250 |
+
ob[name] = info
|
251 |
+
local_bone_data_count += 1
|
252 |
+
self.report(type={'INFO'}, message="Bone Data was pasted, mission accomplished")
|
253 |
+
return {'FINISHED'}
|
254 |
+
|
255 |
+
class remove_armature_bone_data_property(bpy.types.Operator):
|
256 |
+
bl_idname = 'object.remove_armature_bone_data_property'
|
257 |
+
bl_label = "Remove Bone Data"
|
258 |
+
bl_description = "Removes all Bone Data from the armature's custom properties."
|
259 |
+
bl_options = {'REGISTER', 'UNDO'}
|
260 |
+
|
261 |
+
@classmethod
|
262 |
+
def poll(cls, context):
|
263 |
+
ob = context.active_object
|
264 |
+
if ob:
|
265 |
+
if ob.type == 'ARMATURE':
|
266 |
+
arm = ob.data
|
267 |
+
if 'BoneData:0' in arm and 'LocalBoneData:0' in arm:
|
268 |
+
return True
|
269 |
+
return False
|
270 |
+
|
271 |
+
def invoke(self, context, event):
|
272 |
+
return context.window_manager.invoke_props_dialog(self)
|
273 |
+
|
274 |
+
def draw(self, context):
|
275 |
+
self.layout.label(text="Removes all bone Data from the armature's custom properties.", icon='CANCEL')
|
276 |
+
|
277 |
+
def execute(self, context):
|
278 |
+
ob = context.active_object.data
|
279 |
+
pass_count = 0
|
280 |
+
if 'BaseBone' in ob:
|
281 |
+
del ob['BaseBone']
|
282 |
+
for i in range(99999):
|
283 |
+
name = "BoneData:" + str(i)
|
284 |
+
if name in ob:
|
285 |
+
del ob[name]
|
286 |
+
else:
|
287 |
+
pass_count += 1
|
288 |
+
if 10 < pass_count:
|
289 |
+
break
|
290 |
+
pass_count = 0
|
291 |
+
for i in range(99999):
|
292 |
+
name = "LocalBoneData:" + str(i)
|
293 |
+
if name in ob:
|
294 |
+
del ob[name]
|
295 |
+
else:
|
296 |
+
pass_count += 1
|
297 |
+
if 10 < pass_count:
|
298 |
+
break
|
299 |
+
self.report(type={'INFO'}, message="Bone data was removed, mission accomplished")
|
300 |
+
return {'FINISHED'}
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_DATA_PT_modifiers.py
ADDED
@@ -0,0 +1,217 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# �u�v���p�e�B�v�G���A �� �u���f�B�t�@�C�A�v�^�u
|
2 |
+
import os, re, sys, bpy, time, bmesh, mathutils, math
|
3 |
+
from . import common
|
4 |
+
|
5 |
+
# ���j���[���ɍ��ڒlj�
|
6 |
+
def menu_func(self, context):
|
7 |
+
ob = context.active_object
|
8 |
+
if ob:
|
9 |
+
if ob.type == 'MESH':
|
10 |
+
me = ob.data
|
11 |
+
if len(ob.modifiers):
|
12 |
+
self.layout.operator('object.forced_modifier_apply', icon_value=common.preview_collections['main']['KISS'].icon_id)
|
13 |
+
|
14 |
+
class forced_modifier_apply(bpy.types.Operator):
|
15 |
+
bl_idname = 'object.forced_modifier_apply'
|
16 |
+
bl_label = "Force Modifiers"
|
17 |
+
bl_description = "Will force any modifiers if the mesh has shape keys."
|
18 |
+
bl_options = {'REGISTER', 'UNDO'}
|
19 |
+
|
20 |
+
custom_normal_blend = bpy.props.FloatProperty(name="CM3d2 Blending ratio", default=0.5, min=0, max=1, soft_min=0, soft_max=1, step=3, precision=0)
|
21 |
+
is_applies = bpy.props.BoolVectorProperty(name="Apply", size=32, options={'SKIP_SAVE'})
|
22 |
+
|
23 |
+
@classmethod
|
24 |
+
def poll(cls, context):
|
25 |
+
ob = context.active_object
|
26 |
+
return len(ob.modifiers)
|
27 |
+
|
28 |
+
def invoke(self, context, event):
|
29 |
+
ob = context.active_object
|
30 |
+
if len(ob.modifiers) == 0:
|
31 |
+
return {'CANCELLED'}
|
32 |
+
return context.window_manager.invoke_props_dialog(self)
|
33 |
+
|
34 |
+
def draw(self, context):
|
35 |
+
self.layout.prop(self, 'custom_normal_blend', icon='SNAP_NORMAL', slider=True)
|
36 |
+
self.layout.label("Apply")
|
37 |
+
ob = context.active_object
|
38 |
+
for index, mod in enumerate(ob.modifiers):
|
39 |
+
icon = 'MOD_%s' % mod.type
|
40 |
+
try:
|
41 |
+
self.layout.prop(self, 'is_applies', text=mod.name, index=index, icon=icon)
|
42 |
+
except:
|
43 |
+
self.layout.prop(self, 'is_applies', text=mod.name, index=index, icon='MODIFIER')
|
44 |
+
|
45 |
+
if mod.show_viewport:
|
46 |
+
self.is_applies[index] = True
|
47 |
+
|
48 |
+
def execute(self, context):
|
49 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
50 |
+
ob = context.active_object
|
51 |
+
me = ob.data
|
52 |
+
is_shaped = bool(me.shape_keys)
|
53 |
+
|
54 |
+
pre_selected_objects = context.selected_objects[:]
|
55 |
+
pre_mode = ob.mode
|
56 |
+
|
57 |
+
if is_shaped:
|
58 |
+
pre_relative_keys = [s.relative_key.name for s in me.shape_keys.key_blocks]
|
59 |
+
pre_active_shape_key_index = ob.active_shape_key_index
|
60 |
+
|
61 |
+
shape_names = [s.name for s in me.shape_keys.key_blocks]
|
62 |
+
shape_deforms = []
|
63 |
+
for shape in me.shape_keys.key_blocks:
|
64 |
+
shape_deforms.append([shape.data[v.index].co.copy() for v in me.vertices])
|
65 |
+
|
66 |
+
ob.active_shape_key_index = len(me.shape_keys.key_blocks) - 1
|
67 |
+
for i in me.shape_keys.key_blocks[:]:
|
68 |
+
ob.shape_key_remove(ob.active_shape_key)
|
69 |
+
|
70 |
+
new_shape_deforms = []
|
71 |
+
for shape_index, deforms in enumerate(shape_deforms):
|
72 |
+
|
73 |
+
temp_ob = ob.copy()
|
74 |
+
temp_me = me.copy()
|
75 |
+
temp_ob.data = temp_me
|
76 |
+
context.scene.objects.link(temp_ob)
|
77 |
+
|
78 |
+
for vert in temp_me.vertices:
|
79 |
+
vert.co = deforms[vert.index].copy()
|
80 |
+
|
81 |
+
override = context.copy()
|
82 |
+
override['object'] = temp_ob
|
83 |
+
for index, mod in enumerate(temp_ob.modifiers):
|
84 |
+
if self.is_applies[index]:
|
85 |
+
try:
|
86 |
+
bpy.ops.object.modifier_apply(override, modifier=mod.name)
|
87 |
+
except:
|
88 |
+
ob.modifiers.remove(mod)
|
89 |
+
|
90 |
+
new_shape_deforms.append([v.co.copy() for v in temp_me.vertices])
|
91 |
+
|
92 |
+
common.remove_data(temp_ob)
|
93 |
+
common.remove_data(temp_me)
|
94 |
+
|
95 |
+
if ob.active_shape_key_index != 0:
|
96 |
+
ob.active_shape_key_index = 0
|
97 |
+
me.update()
|
98 |
+
|
99 |
+
copy_modifiers = ob.modifiers[:]
|
100 |
+
|
101 |
+
for index, mod in enumerate(copy_modifiers):
|
102 |
+
if self.is_applies[index] and mod.type != 'ARMATURE':
|
103 |
+
|
104 |
+
if mod.type == 'MIRROR':
|
105 |
+
for vg in ob.vertex_groups[:]:
|
106 |
+
replace_list = ((r'\.L$', ".R"), (r'\.R$', ".L"), (r'\.l$', ".r"), (r'\.r$', ".l"), (r'_L$', "_R"), (r'_R$', "_L"), (r'_l$', "_r"), (r'_r$', "_l"))
|
107 |
+
for before, after in replace_list:
|
108 |
+
mirrored_name = re.sub(before, after, vg.name)
|
109 |
+
if mirrored_name not in ob.vertex_groups:
|
110 |
+
ob.vertex_groups.new(mirrored_name)
|
111 |
+
|
112 |
+
try:
|
113 |
+
bpy.ops.object.modifier_apply(modifier=mod.name)
|
114 |
+
except:
|
115 |
+
ob.modifiers.remove(mod)
|
116 |
+
|
117 |
+
arm_ob = None
|
118 |
+
for mod in ob.modifiers:
|
119 |
+
if mod.type == "ARMATURE":
|
120 |
+
arm_ob = mod.object
|
121 |
+
|
122 |
+
if arm_ob:
|
123 |
+
bpy.ops.object.mode_set(mode='EDIT')
|
124 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
125 |
+
|
126 |
+
arm = arm_ob.data
|
127 |
+
arm_pose = arm_ob.pose
|
128 |
+
|
129 |
+
pose_quats = {}
|
130 |
+
for bone in arm.bones:
|
131 |
+
pose_bone = arm_pose.bones[bone.name]
|
132 |
+
|
133 |
+
bone_quat = bone.matrix_local.to_quaternion()
|
134 |
+
pose_quat = pose_bone.matrix.to_quaternion()
|
135 |
+
result_quat = pose_quat * bone_quat.inverted()
|
136 |
+
|
137 |
+
pose_quats[bone.name] = result_quat.copy()
|
138 |
+
|
139 |
+
custom_normals = []
|
140 |
+
for loop in me.loops:
|
141 |
+
vert = me.vertices[loop.vertex_index]
|
142 |
+
no = vert.normal.copy()
|
143 |
+
|
144 |
+
total_weight = 0.0
|
145 |
+
for vge in vert.groups:
|
146 |
+
vg = ob.vertex_groups[vge.group]
|
147 |
+
try:
|
148 |
+
pose_quats[vg.name]
|
149 |
+
except KeyError:
|
150 |
+
continue
|
151 |
+
total_weight += vge.weight
|
152 |
+
|
153 |
+
total_quat = mathutils.Quaternion()
|
154 |
+
for vge in vert.groups:
|
155 |
+
vg = ob.vertex_groups[vge.group]
|
156 |
+
try:
|
157 |
+
total_quat = total_quat.slerp(pose_quats[vg.name], vge.weight / total_weight)
|
158 |
+
except KeyError:
|
159 |
+
pass
|
160 |
+
|
161 |
+
no.rotate(total_quat)
|
162 |
+
custom_normals.append(no)
|
163 |
+
|
164 |
+
for index, mod in enumerate(copy_modifiers):
|
165 |
+
if self.is_applies[index] and mod.type == 'ARMATURE':
|
166 |
+
try:
|
167 |
+
bpy.ops.object.modifier_apply(modifier=mod.name)
|
168 |
+
except:
|
169 |
+
ob.modifiers.remove(mod)
|
170 |
+
|
171 |
+
context.scene.objects.active = ob
|
172 |
+
|
173 |
+
if is_shaped:
|
174 |
+
|
175 |
+
for deforms in new_shape_deforms:
|
176 |
+
if len(me.vertices) != len(deforms):
|
177 |
+
self.report(type={'ERROR'}, message="Since the number of vertices has changed due to mirror etc, The shape key can not be stored. Please undo with Ctrl + Z or other.")
|
178 |
+
return {'CANCELLED'}
|
179 |
+
|
180 |
+
for shape_index, deforms in enumerate(new_shape_deforms):
|
181 |
+
|
182 |
+
bpy.ops.object.shape_key_add(from_mix=False)
|
183 |
+
shape = ob.active_shape_key
|
184 |
+
shape.name = shape_names[shape_index]
|
185 |
+
|
186 |
+
for vert in me.vertices:
|
187 |
+
shape.data[vert.index].co = deforms[vert.index].copy()
|
188 |
+
|
189 |
+
for shape_index, shape in enumerate(me.shape_keys.key_blocks):
|
190 |
+
shape.relative_key = me.shape_keys.key_blocks[pre_relative_keys[shape_index]]
|
191 |
+
|
192 |
+
ob.active_shape_key_index = pre_active_shape_key_index
|
193 |
+
|
194 |
+
for temp_ob in pre_selected_objects:
|
195 |
+
temp_ob.select = True
|
196 |
+
bpy.ops.object.mode_set(mode=pre_mode)
|
197 |
+
|
198 |
+
if arm_ob:
|
199 |
+
for i, loop in enumerate(me.loops):
|
200 |
+
vert = me.vertices[loop.vertex_index]
|
201 |
+
no = vert.normal.copy()
|
202 |
+
|
203 |
+
try:
|
204 |
+
custom_rot = mathutils.Vector((0.0, 0.0, 1.0)).rotation_difference(custom_normals[i])
|
205 |
+
except:
|
206 |
+
continue
|
207 |
+
original_rot = mathutils.Vector((0.0, 0.0, 1.0)).rotation_difference(no)
|
208 |
+
output_rot = original_rot.slerp(custom_rot, self.custom_normal_blend)
|
209 |
+
|
210 |
+
output_no = mathutils.Vector((0.0, 0.0, 1.0))
|
211 |
+
output_no.rotate(output_rot)
|
212 |
+
|
213 |
+
custom_normals[i] = output_no
|
214 |
+
me.use_auto_smooth = True
|
215 |
+
me.normals_split_custom_set(custom_normals)
|
216 |
+
|
217 |
+
return {'FINISHED'}
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_DATA_PT_vertex_groups.py
ADDED
@@ -0,0 +1,127 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 「プロパティ」エリア → 「メッシュデータ」タブ → 「頂点グループ」パネル
|
2 |
+
import os, re, sys, bpy, time, bmesh, mathutils
|
3 |
+
from . import common
|
4 |
+
|
5 |
+
# メニュー等に項目追加
|
6 |
+
def menu_func(self, context):
|
7 |
+
import re
|
8 |
+
ob = context.active_object
|
9 |
+
if not ob: return
|
10 |
+
if not len(ob.vertex_groups) and ob.type != 'MESH': return
|
11 |
+
|
12 |
+
flag = False
|
13 |
+
for vertex_group in ob.vertex_groups:
|
14 |
+
if not flag and re.search(r'[_ ]([rRlL])[_ ]', vertex_group.name):
|
15 |
+
flag = True
|
16 |
+
if not flag and vertex_group.name.count('*') == 1:
|
17 |
+
if re.search(r'\.([rRlL])$', vertex_group.name):
|
18 |
+
flag = True
|
19 |
+
if flag:
|
20 |
+
col = self.layout.column(align=True)
|
21 |
+
col.label(text="Convert names for CM3D2", icon_value=common.preview_collections['main']['KISS'].icon_id)
|
22 |
+
row = col.row(align=True)
|
23 |
+
row.operator('object.decode_cm3d2_vertex_group_names', icon='BLENDER', text="CM3D2 → Blender")
|
24 |
+
row.operator('object.encode_cm3d2_vertex_group_names', icon_value=common.preview_collections['main']['KISS'].icon_id, text="Blender → CM3D2")
|
25 |
+
break
|
26 |
+
|
27 |
+
class decode_cm3d2_vertex_group_names(bpy.types.Operator):
|
28 |
+
bl_idname = 'object.decode_cm3d2_vertex_group_names'
|
29 |
+
bl_label = "Convert Vertex Group Names for Blender"
|
30 |
+
bl_description = "Names are converted for use with Blender's mirror functions."
|
31 |
+
bl_options = {'REGISTER', 'UNDO'}
|
32 |
+
|
33 |
+
@classmethod
|
34 |
+
def poll(cls, context):
|
35 |
+
import re
|
36 |
+
ob = context.active_object
|
37 |
+
if ob:
|
38 |
+
if ob.type == 'MESH':
|
39 |
+
if ob.vertex_groups.active:
|
40 |
+
for vg in ob.vertex_groups:
|
41 |
+
if re.search(r'[_ ]([rRlL])[_ ]', vg.name):
|
42 |
+
return True
|
43 |
+
return False
|
44 |
+
|
45 |
+
def execute(self, context):
|
46 |
+
ob = context.active_object
|
47 |
+
me = ob.data
|
48 |
+
convert_count = 0
|
49 |
+
context.window_manager.progress_begin(0, len(ob.vertex_groups))
|
50 |
+
for vg_index, vg in enumerate(ob.vertex_groups[:]):
|
51 |
+
context.window_manager.progress_update(vg_index)
|
52 |
+
vg_name = common.decode_bone_name(vg.name)
|
53 |
+
if vg_name != vg.name:
|
54 |
+
if vg_name in ob.vertex_groups:
|
55 |
+
target_vg = ob.vertex_groups[vg_name]
|
56 |
+
for vert in me.vertices:
|
57 |
+
try:
|
58 |
+
weight = vg.weight(vert.index)
|
59 |
+
except:
|
60 |
+
weight = 0.0
|
61 |
+
try:
|
62 |
+
target_weight = target_vg.weight(vert.index)
|
63 |
+
except:
|
64 |
+
target_weight = 0.0
|
65 |
+
if 0.0 < weight + target_weight:
|
66 |
+
target_vg.add([vert.index], weight + target_weight, 'REPLACE')
|
67 |
+
ob.vertex_groups.remove(vg)
|
68 |
+
else:
|
69 |
+
vg.name = vg_name
|
70 |
+
convert_count += 1
|
71 |
+
if convert_count == 0:
|
72 |
+
self.report(type={'WARNING'}, message="A Name that could not be converted was found. Mission Failed.")
|
73 |
+
else:
|
74 |
+
self.report(type={'INFO'}, message="Vertex group names were converted for Blender. Mission Accomplished.")
|
75 |
+
context.window_manager.progress_end()
|
76 |
+
return {'FINISHED'}
|
77 |
+
|
78 |
+
class encode_cm3d2_vertex_group_names(bpy.types.Operator):
|
79 |
+
bl_idname = 'object.encode_cm3d2_vertex_group_names'
|
80 |
+
bl_label = "Convert vertex group names for CM3D2"
|
81 |
+
bl_description = "Converts bone names for CM3D2."
|
82 |
+
bl_options = {'REGISTER', 'UNDO'}
|
83 |
+
|
84 |
+
@classmethod
|
85 |
+
def poll(cls, context):
|
86 |
+
import re
|
87 |
+
ob = context.active_object
|
88 |
+
if ob:
|
89 |
+
if ob.type == 'MESH':
|
90 |
+
if ob.vertex_groups.active:
|
91 |
+
for vg in ob.vertex_groups:
|
92 |
+
if vg.name.count('*') == 1 and re.search(r'\.([rRlL])$', vg.name):
|
93 |
+
return True
|
94 |
+
return False
|
95 |
+
|
96 |
+
def execute(self, context):
|
97 |
+
ob = context.active_object
|
98 |
+
me = ob.data
|
99 |
+
convert_count = 0
|
100 |
+
context.window_manager.progress_begin(0, len(ob.vertex_groups))
|
101 |
+
for vg_index, vg in enumerate(ob.vertex_groups[:]):
|
102 |
+
context.window_manager.progress_update(vg_index)
|
103 |
+
vg_name = common.encode_bone_name(vg.name)
|
104 |
+
if vg_name != vg.name:
|
105 |
+
if vg_name in ob.vertex_groups:
|
106 |
+
target_vg = ob.vertex_groups[vg_name]
|
107 |
+
for vert in me.vertices:
|
108 |
+
try:
|
109 |
+
weight = vg.weight(vert.index)
|
110 |
+
except:
|
111 |
+
weight = 0.0
|
112 |
+
try:
|
113 |
+
target_weight = target_vg.weight(vert.index)
|
114 |
+
except:
|
115 |
+
target_weight = 0.0
|
116 |
+
if 0.0 < weight + target_weight:
|
117 |
+
target_vg.add([vert.index], weight + target_weight, 'REPLACE')
|
118 |
+
ob.vertex_groups.remove(vg)
|
119 |
+
else:
|
120 |
+
vg.name = vg_name
|
121 |
+
convert_count += 1
|
122 |
+
if convert_count == 0:
|
123 |
+
self.report(type={'WARNING'}, message="A Name that could not be converted was found. Mission Failed.")
|
124 |
+
else:
|
125 |
+
self.report(type={'INFO'}, message="Names were converted for CM3D2. Mission Accomplished")
|
126 |
+
context.window_manager.progress_end()
|
127 |
+
return {'FINISHED'}
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_IMAGE_HT_header.py
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 「UV/画像エディター」エリア → ヘッダー
|
2 |
+
import os, re, sys, bpy, time, bmesh, mathutils
|
3 |
+
from . import common
|
4 |
+
|
5 |
+
# メニュー等に項目追加
|
6 |
+
def menu_func(self, context):
|
7 |
+
if 'edit_image' in dir(context):
|
8 |
+
img = context.edit_image
|
9 |
+
if img:
|
10 |
+
if 'cm3d2_path' in img:
|
11 |
+
self.layout.label(text="For CM3D2: Internal Path", icon_value=common.preview_collections['main']['KISS'].icon_id)
|
12 |
+
row = self.layout.row()
|
13 |
+
row.prop(img, '["cm3d2_path"]', text="")
|
14 |
+
row.scale_x = 3.0
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_IMAGE_PT_image_properties.py
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 「UV/画像エディター」エリア → プロパティ → 「画像」パネル
|
2 |
+
import os, re, sys, bpy, time, bmesh, mathutils
|
3 |
+
from . import common
|
4 |
+
|
5 |
+
# メニュー等に項目追加
|
6 |
+
def menu_func(self, context):
|
7 |
+
if 'edit_image' in dir(context):
|
8 |
+
img = context.edit_image
|
9 |
+
if 'cm3d2_path' in img:
|
10 |
+
box = self.layout.box()
|
11 |
+
box.label(text="For CM3D2", icon_value=common.preview_collections['main']['KISS'].icon_id)
|
12 |
+
box.prop(img, '["cm3d2_path"]', icon='ANIM_DATA', text="Internal Path")
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_INFO_HT_header.py
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 画面右上 (「情報」エリア → ヘッダー)
|
2 |
+
import bpy, bmesh
|
3 |
+
from . import common
|
4 |
+
|
5 |
+
# メニュー等に項目追加
|
6 |
+
def menu_func(self, context):
|
7 |
+
self.layout.operator('mesh.vertices_count_checker', icon_value=common.preview_collections['main']['KISS'].icon_id)
|
8 |
+
|
9 |
+
class vertices_count_checker(bpy.types.Operator):
|
10 |
+
bl_idname = 'mesh.vertices_count_checker'
|
11 |
+
bl_label = "Check Vertice Count"
|
12 |
+
bl_description = "Check whether the exporter can output the selected mesh."
|
13 |
+
bl_options = {'REGISTER', 'UNDO'}
|
14 |
+
|
15 |
+
@classmethod
|
16 |
+
def poll(cls, context):
|
17 |
+
ob = context.active_object
|
18 |
+
if ob:
|
19 |
+
if ob.type == 'MESH':
|
20 |
+
return True
|
21 |
+
return False
|
22 |
+
|
23 |
+
def execute(self, context):
|
24 |
+
me = context.object.data
|
25 |
+
if not me.uv_layers.active:
|
26 |
+
self.report(type={'ERROR'}, message="No UV Map. Cannot be Counted.")
|
27 |
+
return {'FINISHED'}
|
28 |
+
bm = bmesh.new()
|
29 |
+
bm.from_mesh(me)
|
30 |
+
|
31 |
+
alreadys = {}
|
32 |
+
uv_lay = bm.loops.layers.uv.active
|
33 |
+
|
34 |
+
for face in bm.faces:
|
35 |
+
for loop in face.loops:
|
36 |
+
info = (loop.vert.index, loop[uv_lay].uv.x, loop[uv_lay].uv.y)
|
37 |
+
if info not in alreadys:
|
38 |
+
alreadys[info] = None
|
39 |
+
bm.free()
|
40 |
+
|
41 |
+
inner_count = len(alreadys)
|
42 |
+
real_count = len(me.vertices)
|
43 |
+
if inner_count <= 65535:
|
44 |
+
self.report(type={'ERROR'}, message="Good, There is space for more vertices, you may add %d more vertices (Vertices:%d(+%d) UV Splitting:+%d%)" % (65535 - inner_count, real_count, inner_count - real_count, int(inner_count / real_count * 100)))
|
45 |
+
else:
|
46 |
+
self.report(type={'ERROR'}, message="X, Too many vertices、please remove %d Vertices (Vertices:%d(+%d) Uv Splitting:+%d%)" % (inner_count - 65535, real_count, inner_count - real_count, int(inner_count / real_count * 100)))
|
47 |
+
|
48 |
+
return {'FINISHED'}
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_INFO_MT_add.py
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 「3Dビュー」エリア → 追加(Shift+A) → CM3D2
|
2 |
+
import os, re, sys, bpy, time, bmesh, mathutils
|
3 |
+
from . import common
|
4 |
+
|
5 |
+
# メニュー等に項目追加
|
6 |
+
def menu_func(self, context):
|
7 |
+
self.layout.separator()
|
8 |
+
self.layout.menu('misc_INFO_MT_add_cm3d2', icon_value=common.preview_collections['main']['KISS'].icon_id)
|
9 |
+
|
10 |
+
# サブメニュー
|
11 |
+
class misc_INFO_MT_add_cm3d2(bpy.types.Menu):
|
12 |
+
bl_idname = 'misc_INFO_MT_add_cm3d2'
|
13 |
+
bl_label = "CM3D2"
|
14 |
+
|
15 |
+
def draw(self, context):
|
16 |
+
self.layout.operator('wm.append_cm3d2_figure', text="body001", icon_value=common.preview_collections['main']['KISS'].icon_id).object_name = "body001.body"
|
17 |
+
self.layout.separator()
|
18 |
+
self.layout.operator('wm.append_cm3d2_figure', text="Large boob shapekey Body", icon='ROTATECOLLECTION').object_name = "乳袋防止素体"
|
19 |
+
self.layout.separator()
|
20 |
+
self.layout.operator('wm.append_cm3d2_figure', text="T Pose Body", icon='ARMATURE_DATA').object_name = "Tスタンス素体"
|
21 |
+
self.layout.operator('wm.append_cm3d2_figure', text="Legs only T pose Body", icon='SOUND').object_name = "Tスタンス素体 足のみ"
|
22 |
+
self.layout.operator('wm.append_cm3d2_figure', text="Arms only T pose Body", icon='OUTLINER_DATA_ARMATURE').object_name = "Tスタンス素体 手のみ"
|
23 |
+
self.layout.separator()
|
24 |
+
self.layout.operator('wm.append_cm3d2_figure', text="Rig for anm output", icon='OUTLINER_OB_ARMATURE').object_name = "anm出力用リグ・身体メッシュ"
|
25 |
+
self.layout.operator('wm.append_cm3d2_figure', text="Alternative Rig (Neerhom's)", icon='OUTLINER_OB_ARMATURE').object_name = "alt_rig_armature"
|
26 |
+
self.layout.operator('wm.append_cm3d2_figure', text="Rig for anm output(Man)", icon='MOD_ARMATURE').object_name = "anm出力用リグ(男)・身体メッシュ"
|
27 |
+
|
28 |
+
class append_cm3d2_figure(bpy.types.Operator):
|
29 |
+
bl_idname = 'wm.append_cm3d2_figure'
|
30 |
+
bl_label = "Import CM3D2 Body"
|
31 |
+
bl_description = "Allows you to import the Body from CM3D2. (Warning: Will not work well with posing and animations.)"
|
32 |
+
bl_options = {'REGISTER', 'UNDO'}
|
33 |
+
|
34 |
+
object_name = bpy.props.StringProperty(name="素体名")
|
35 |
+
|
36 |
+
def execute(self, context):
|
37 |
+
if bpy.ops.object.mode_set.poll():
|
38 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
39 |
+
if bpy.ops.object.select_all.poll():
|
40 |
+
bpy.ops.object.select_all(action='DESELECT')
|
41 |
+
|
42 |
+
blend_path = os.path.join(os.path.dirname(__file__), "append_data.blend")
|
43 |
+
with context.blend_data.libraries.load(blend_path) as (data_from, data_to):
|
44 |
+
data_to.objects = [self.object_name]
|
45 |
+
|
46 |
+
ob = data_to.objects[0]
|
47 |
+
context.scene.objects.link(ob)
|
48 |
+
context.scene.objects.active = ob
|
49 |
+
ob.select = True
|
50 |
+
|
51 |
+
for mod in ob.modifiers:
|
52 |
+
if mod.type == 'ARMATURE':
|
53 |
+
context.scene.objects.link(mod.object)
|
54 |
+
mod.object.select = True
|
55 |
+
|
56 |
+
return {'FINISHED'}
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_INFO_MT_curve_add.py
ADDED
@@ -0,0 +1,186 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 「3Dビュー」エリア → 追加(Shift+A) → カーブ
|
2 |
+
import os, re, sys, bpy, time, bmesh, mathutils
|
3 |
+
from . import common
|
4 |
+
|
5 |
+
# メニュー等に項目追加
|
6 |
+
def menu_func(self, context):
|
7 |
+
self.layout.separator()
|
8 |
+
self.layout.operator('curve.hair_bunch_add', text="Add Hair Curve", icon_value=common.preview_collections['main']['KISS'].icon_id)
|
9 |
+
|
10 |
+
class hair_bunch_add(bpy.types.Operator):
|
11 |
+
bl_idname = 'curve.hair_bunch_add'
|
12 |
+
bl_label = "Add A Lock of Hair"
|
13 |
+
bl_description = "Will add an anime style hair lock at the 3D Cursor."
|
14 |
+
bl_options = {'REGISTER', 'UNDO'}
|
15 |
+
|
16 |
+
radius = bpy.props.FloatProperty(name="Radius", default=0.1, min=0, max=10, soft_min=0, soft_max=10, step=10, precision=2)
|
17 |
+
random_multi = bpy.props.FloatProperty(name="Randomness", default=0.5, min=0, max=10, soft_min=0, soft_max=10, step=10, precision=2)
|
18 |
+
z_plus = bpy.props.FloatProperty(name="Medium Z Axis Height", default=0.1, min=0, max=10, soft_min=0, soft_max=10, step=10, precision=2)
|
19 |
+
|
20 |
+
@classmethod
|
21 |
+
def poll(cls, context):
|
22 |
+
return True
|
23 |
+
|
24 |
+
def invoke(self, context, event):
|
25 |
+
import bpy_extras.view3d_utils
|
26 |
+
|
27 |
+
self.pre_draw = bpy.types.VIEW3D_HT_header.draw
|
28 |
+
def header_draw(self, context):
|
29 |
+
row = self.layout.row(align=True)
|
30 |
+
row.label(text="Mouse Wheel:Change Thickness")
|
31 |
+
row.label(text="Middle Mouse Button:Random Intensity Change")
|
32 |
+
row.label(text="Z/X Keys:Height Change")
|
33 |
+
bpy.types.VIEW3D_HT_header.draw = header_draw
|
34 |
+
|
35 |
+
if context.active_object:
|
36 |
+
if context.active_object.mode != 'OBJECT':
|
37 |
+
self.report(type={'ERROR'}, message="Please add in Object mode only.")
|
38 |
+
return {'CANCELLED'}
|
39 |
+
|
40 |
+
self.end_location = bpy_extras.view3d_utils.region_2d_to_location_3d(context.region, context.region_data, (event.mouse_region_x, event.mouse_region_y), context.space_data.cursor_location)
|
41 |
+
|
42 |
+
curve = context.blend_data.curves.new("Hair Bunch", 'CURVE')
|
43 |
+
ob = context.blend_data.objects.new("Hair Bunch", curve)
|
44 |
+
context.scene.objects.link(ob)
|
45 |
+
context.scene.objects.active = ob
|
46 |
+
ob.select = True
|
47 |
+
|
48 |
+
curve.dimensions = '3D'
|
49 |
+
curve.resolution_u = 5
|
50 |
+
|
51 |
+
spline = curve.splines.new('NURBS')
|
52 |
+
|
53 |
+
spline.points.add(3)
|
54 |
+
spline.points[0].radius = 0.0
|
55 |
+
spline.points[-1].radius = 0.0
|
56 |
+
spline.use_endpoint_u = True
|
57 |
+
spline.order_u = 4
|
58 |
+
spline.resolution_u = 5
|
59 |
+
|
60 |
+
self.set_spline(spline, context)
|
61 |
+
|
62 |
+
self.object = ob
|
63 |
+
self.curve = curve
|
64 |
+
self.spline = spline
|
65 |
+
|
66 |
+
bevel_curve = context.blend_data.curves.new("Hair Bunch Bevel", 'CURVE')
|
67 |
+
bevel_ob = context.blend_data.objects.new("Hair Bunch Bevel", bevel_curve)
|
68 |
+
context.scene.objects.link(bevel_ob)
|
69 |
+
bevel_ob.select = True
|
70 |
+
curve.bevel_object = bevel_ob
|
71 |
+
|
72 |
+
bevel_ob.parent = ob
|
73 |
+
bevel_ob.parent_type = 'VERTEX'
|
74 |
+
bevel_ob.parent_vertices = (3, 3, 3)
|
75 |
+
|
76 |
+
bevel_curve.dimensions = '2D'
|
77 |
+
bevel_curve.fill_mode = 'NONE'
|
78 |
+
bevel_curve.resolution_u = 2
|
79 |
+
|
80 |
+
spline = bevel_curve.splines.new('NURBS')
|
81 |
+
spline.points.add(7)
|
82 |
+
spline.use_cyclic_u = True
|
83 |
+
spline.order_u = 4
|
84 |
+
spline.resolution_u = 2
|
85 |
+
|
86 |
+
self.bevel_object = bevel_ob
|
87 |
+
self.bevel_curve = bevel_curve
|
88 |
+
self.bevel_spline = spline
|
89 |
+
|
90 |
+
self.set_bevel_spline(spline)
|
91 |
+
|
92 |
+
context.window_manager.modal_handler_add(self)
|
93 |
+
return {'RUNNING_MODAL'}
|
94 |
+
|
95 |
+
def modal(self, context, event):
|
96 |
+
import bpy_extras.view3d_utils
|
97 |
+
|
98 |
+
#print(event.type, event.value)
|
99 |
+
|
100 |
+
if event.type == 'MOUSEMOVE':
|
101 |
+
self.end_location = bpy_extras.view3d_utils.region_2d_to_location_3d(context.region, context.region_data, (event.mouse_region_x, event.mouse_region_y), context.space_data.cursor_location)
|
102 |
+
self.execute(context)
|
103 |
+
|
104 |
+
elif event.type == 'WHEELUPMOUSE' and event.value == 'PRESS':
|
105 |
+
self.radius += 0.05
|
106 |
+
self.set_bevel_spline(self.bevel_spline)
|
107 |
+
self.object.update_tag({'OBJECT', 'DATA'})
|
108 |
+
elif event.type == 'WHEELDOWNMOUSE' and event.value == 'PRESS':
|
109 |
+
self.radius -= 0.05
|
110 |
+
self.set_bevel_spline(self.bevel_spline)
|
111 |
+
self.object.update_tag({'OBJECT', 'DATA'})
|
112 |
+
|
113 |
+
elif event.type == 'MIDDLEMOUSE' and event.value == 'PRESS':
|
114 |
+
if 0.9 < self.random_multi:
|
115 |
+
self.random_multi = 0.0
|
116 |
+
elif 0.4 < self.random_multi:
|
117 |
+
self.random_multi = 1.0
|
118 |
+
else:
|
119 |
+
self.random_multi = 0.5
|
120 |
+
self.set_bevel_spline(self.bevel_spline)
|
121 |
+
|
122 |
+
elif event.type == 'Z' and event.value == 'PRESS':
|
123 |
+
self.z_plus += 0.1
|
124 |
+
self.execute(context)
|
125 |
+
elif event.type == 'X' and event.value == 'PRESS':
|
126 |
+
self.z_plus -= 0.1
|
127 |
+
self.execute(context)
|
128 |
+
|
129 |
+
elif event.type == 'LEFTMOUSE' and event.value == 'PRESS':
|
130 |
+
bpy.types.VIEW3D_HT_header.draw = self.pre_draw
|
131 |
+
context.area.tag_redraw()
|
132 |
+
return {'FINISHED'}
|
133 |
+
|
134 |
+
elif event.type in {'RIGHTMOUSE', 'ESC'} and event.value == 'PRESS':
|
135 |
+
common.remove_data([self.object, self.bevel_object, self.curve, self.bevel_curve])
|
136 |
+
bpy.types.VIEW3D_HT_header.draw = self.pre_draw
|
137 |
+
context.area.tag_redraw()
|
138 |
+
return {'CANCELLED'}
|
139 |
+
|
140 |
+
return {'RUNNING_MODAL'}
|
141 |
+
|
142 |
+
def get_random_point(self, co):
|
143 |
+
import random
|
144 |
+
r = self.radius * self.random_multi
|
145 |
+
co.x = co.x + random.uniform(-r, r)
|
146 |
+
co.y = co.y + random.uniform(-r, r)
|
147 |
+
return co
|
148 |
+
|
149 |
+
def set_bevel_spline(self, spline):
|
150 |
+
import math, mathutils
|
151 |
+
r = self.radius
|
152 |
+
vec = mathutils.Vector((0, r, 0))
|
153 |
+
min_rad = -math.radians(360 / len(spline.points))
|
154 |
+
for index, point in enumerate(spline.points):
|
155 |
+
eul = mathutils.Euler((0, 0, min_rad * index), 'XYZ')
|
156 |
+
now_vec = vec.copy()
|
157 |
+
now_vec.rotate(eul)
|
158 |
+
now_vec = self.get_random_point(now_vec)
|
159 |
+
point.co = list(now_vec[:]) + [1]
|
160 |
+
|
161 |
+
def set_spline(self, spline, context):
|
162 |
+
diff_co = self.end_location - context.space_data.cursor_location
|
163 |
+
|
164 |
+
plus_co = diff_co * 0.333333
|
165 |
+
plus_co.z = -plus_co.z + self.z_plus
|
166 |
+
|
167 |
+
point1 = diff_co * 0.333333
|
168 |
+
point1 += plus_co * 1
|
169 |
+
point1 += context.space_data.cursor_location
|
170 |
+
|
171 |
+
point2 = diff_co * 0.666666
|
172 |
+
point2 += plus_co * 1
|
173 |
+
point2 += context.space_data.cursor_location
|
174 |
+
|
175 |
+
spline.points[0].co = list(context.space_data.cursor_location[:]) + [1]
|
176 |
+
spline.points[1].co = list(point1[:]) + [1]
|
177 |
+
spline.points[2].co = list(point2[:]) + [1]
|
178 |
+
spline.points[-1].co = list(self.end_location[:]) + [1]
|
179 |
+
|
180 |
+
def execute(self, context):
|
181 |
+
try:
|
182 |
+
self.set_spline(self.spline, context)
|
183 |
+
except:
|
184 |
+
self.report(type={'ERROR'}, message="Please run in Object mode. Note: Script is Buggy.")
|
185 |
+
return {'CANCELLED'}
|
186 |
+
return {'FINISHED'}
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_INFO_MT_help.py
ADDED
@@ -0,0 +1,154 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 画面上部 (「情報」エリア → ヘッダー) → ヘルプ
|
2 |
+
import os, re, sys, bpy, time, bmesh, mathutils
|
3 |
+
from . import common
|
4 |
+
|
5 |
+
# メニュー等に項目追加
|
6 |
+
def menu_func(self, context):
|
7 |
+
icon_id = common.preview_collections['main']['KISS'].icon_id
|
8 |
+
self.layout.separator()
|
9 |
+
self.layout.operator('script.update_cm3d2_converter', icon_value=icon_id)
|
10 |
+
self.layout.operator('wm.call_menu', icon_value=icon_id, text="CM3D2 Converter Update History").name = 'INFO_MT_help_CM3D2_Converter_RSS'
|
11 |
+
self.layout.operator('wm.show_cm3d2_converter_preference', icon_value=icon_id)
|
12 |
+
|
13 |
+
# 更新履歴メニュー
|
14 |
+
class INFO_MT_help_CM3D2_Converter_RSS(bpy.types.Menu):
|
15 |
+
bl_idname = 'INFO_MT_help_CM3D2_Converter_RSS'
|
16 |
+
bl_label = "CM3D2 Converter Update History"
|
17 |
+
|
18 |
+
def draw(self, context):
|
19 |
+
try:
|
20 |
+
import re, urllib, datetime, urllib.request, xml.sax.saxutils
|
21 |
+
response = urllib.request.urlopen("https://github.com/CM3Duser/Blender-CM3D2-Converter/commits/master.atom")
|
22 |
+
html = response.read().decode('utf-8')
|
23 |
+
titles = re.findall(r'\<title\>[ \s]*([^ \s][^\<]*[^ \s])[ \s]*\<\/title\>', html)[1:]
|
24 |
+
updates = re.findall(r'\<updated\>([^\<\>]*)\<\/updated\>', html)[1:]
|
25 |
+
links = re.findall(r'<link [^\<\>]*href="([^"]+)"/>', html)[2:]
|
26 |
+
version_datetime = datetime.datetime.strptime(str(common.bl_info["version"][0]) + "," + str(common.bl_info["version"][1]) + "," + str(common.bl_info["version"][2]) + "," + str(common.bl_info["version"][3]) + "," + str(common.bl_info["version"][4]) + "," + str(common.bl_info["version"][5]), '%Y,%m,%d,%H,%M,%S')
|
27 |
+
|
28 |
+
output_data = []
|
29 |
+
update_diffs = []
|
30 |
+
for title, update, link in zip(titles, updates, links):
|
31 |
+
title = xml.sax.saxutils.unescape(title, {'"': '"'})
|
32 |
+
|
33 |
+
rss_datetime = datetime.datetime.strptime(update, '%Y-%m-%dT%H:%M:%SZ') + datetime.timedelta(hours=9)
|
34 |
+
diff_seconds = datetime.datetime.now() - rss_datetime
|
35 |
+
icon = 'SORTTIME'
|
36 |
+
if 60 * 60 * 24 * 7 < diff_seconds.total_seconds():
|
37 |
+
icon = 'NLA'
|
38 |
+
elif 60 * 60 * 24 * 3 < diff_seconds.total_seconds():
|
39 |
+
icon = 'COLLAPSEMENU'
|
40 |
+
elif 60 * 60 * 24 < diff_seconds.total_seconds():
|
41 |
+
icon = 'TIME'
|
42 |
+
elif 60 * 60 < diff_seconds.total_seconds():
|
43 |
+
icon = 'RECOVER_LAST'
|
44 |
+
else:
|
45 |
+
icon = 'PREVIEW_RANGE'
|
46 |
+
|
47 |
+
if 60 * 60 * 24 <= diff_seconds.total_seconds():
|
48 |
+
date_str = "%d日前" % int(diff_seconds.total_seconds() / 60 / 60 / 24)
|
49 |
+
elif 60 * 60 <= diff_seconds.total_seconds():
|
50 |
+
date_str = "%d時間前" % int(diff_seconds.total_seconds() / 60 / 60)
|
51 |
+
elif 60 <= diff_seconds.total_seconds():
|
52 |
+
date_str = "%d分前" % int(diff_seconds.total_seconds() / 60)
|
53 |
+
else:
|
54 |
+
date_str = "%d秒前" % diff_seconds.total_seconds()
|
55 |
+
|
56 |
+
text = "(" + date_str + ") " + title
|
57 |
+
|
58 |
+
update_diff = abs( (version_datetime - rss_datetime).total_seconds() )
|
59 |
+
|
60 |
+
output_data.append((text, icon, link, update_diff))
|
61 |
+
update_diffs.append(update_diff)
|
62 |
+
|
63 |
+
min_update_diff = sorted(update_diffs)[0]
|
64 |
+
for text, icon, link, update_diff in output_data:
|
65 |
+
|
66 |
+
if update_diff == min_update_diff:
|
67 |
+
text = "Now! " + text
|
68 |
+
icon = 'QUESTION'
|
69 |
+
|
70 |
+
self.layout.operator('wm.url_open', text=text, icon=icon).url = link
|
71 |
+
except:
|
72 |
+
self.layout.label(text="Failed to Download Update.", icon='ERROR')
|
73 |
+
|
74 |
+
class update_cm3d2_converter(bpy.types.Operator):
|
75 |
+
bl_idname = 'script.update_cm3d2_converter'
|
76 |
+
bl_label = "Update CM3D2 Converter (WARNING: COULD REMOVE TRANSLATIONS)"
|
77 |
+
bl_description = "Will quickly download the latest CM3D2 Converter from the Github Page."
|
78 |
+
bl_options = {'REGISTER'}
|
79 |
+
|
80 |
+
is_restart = bpy.props.BoolProperty(name="Restart Blender After Updating", default=True)
|
81 |
+
is_toggle_console = bpy.props.BoolProperty(name="Close the Console after Restart", default=True)
|
82 |
+
|
83 |
+
def invoke(self, context, event):
|
84 |
+
return context.window_manager.invoke_props_dialog(self)
|
85 |
+
|
86 |
+
def draw(self, context):
|
87 |
+
self.layout.menu('INFO_MT_help_CM3D2_Converter_RSS', icon='INFO')
|
88 |
+
self.layout.prop(self, 'is_restart', icon='BLENDER')
|
89 |
+
self.layout.prop(self, 'is_toggle_console', icon='CONSOLE')
|
90 |
+
|
91 |
+
def execute(self, context):
|
92 |
+
import os, sys, urllib, zipfile, subprocess, urllib.request
|
93 |
+
|
94 |
+
zip_path = os.path.join(bpy.app.tempdir, "Blender-CM3D2-Converter-master.zip")
|
95 |
+
addon_path = os.path.dirname(__file__)
|
96 |
+
|
97 |
+
response = urllib.request.urlopen("https://github.com/CM3Duser/Blender-CM3D2-Converter/archive/master.zip")
|
98 |
+
zip_file = open(zip_path, "wb")
|
99 |
+
zip_file.write(response.read())
|
100 |
+
zip_file.close()
|
101 |
+
|
102 |
+
zip_file = zipfile.ZipFile(zip_path, "r")
|
103 |
+
for path in zip_file.namelist():
|
104 |
+
if not os.path.basename(path):
|
105 |
+
continue
|
106 |
+
sub_dir = os.path.split( os.path.split(path)[0] )[1]
|
107 |
+
if sub_dir == "CM3D2 Converter":
|
108 |
+
file = open(os.path.join(addon_path, os.path.basename(path)), 'wb')
|
109 |
+
file.write(zip_file.read(path))
|
110 |
+
file.close()
|
111 |
+
zip_file.close()
|
112 |
+
|
113 |
+
if self.is_restart:
|
114 |
+
filepath = bpy.data.filepath
|
115 |
+
command_line = [sys.argv[0]]
|
116 |
+
if filepath:
|
117 |
+
command_line.append(filepath)
|
118 |
+
if self.is_toggle_console:
|
119 |
+
py = os.path.join(os.path.dirname(__file__), "console_toggle.py")
|
120 |
+
command_line.append('-P')
|
121 |
+
command_line.append(py)
|
122 |
+
subprocess.Popen(command_line)
|
123 |
+
bpy.ops.wm.quit_blender()
|
124 |
+
else:
|
125 |
+
self.report(type={'INFO'}, message="Converter Updated. Please Reboot Blender.")
|
126 |
+
return {'FINISHED'}
|
127 |
+
|
128 |
+
class show_cm3d2_converter_preference(bpy.types.Operator):
|
129 |
+
bl_idname = 'wm.show_cm3d2_converter_preference'
|
130 |
+
bl_label = "CM3D2 Converter Settings Screen"
|
131 |
+
bl_description = "Will open the plugin's settings in the addon window."
|
132 |
+
bl_options = {'REGISTER', 'UNDO'}
|
133 |
+
|
134 |
+
def execute(self, context):
|
135 |
+
import addon_utils
|
136 |
+
my_info = None
|
137 |
+
for module in addon_utils.modules():
|
138 |
+
info = addon_utils.module_bl_info(module)
|
139 |
+
if info['name'] == common.addon_name:
|
140 |
+
my_info = info
|
141 |
+
break
|
142 |
+
area = common.get_request_area(context, 'USER_PREFERENCES')
|
143 |
+
if area and my_info:
|
144 |
+
context.user_preferences.active_section = 'ADDONS'
|
145 |
+
context.window_manager.addon_search = my_info['name']
|
146 |
+
context.window_manager.addon_filter = 'All'
|
147 |
+
if 'COMMUNITY' not in context.window_manager.addon_support:
|
148 |
+
context.window_manager.addon_support = {'OFFICIAL', 'COMMUNITY'}
|
149 |
+
if not my_info['show_expanded']:
|
150 |
+
bpy.ops.wm.addon_expand(module=__name__.split('.')[0])
|
151 |
+
else:
|
152 |
+
self.report(type={'ERROR'}, message="Could not open the settings window.")
|
153 |
+
return {'CANCELLED'}
|
154 |
+
return {'FINISHED'}
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_MATERIAL_PT_context_material.py
ADDED
@@ -0,0 +1,822 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 「プロパティ」エリア → 「マテリアル」タブ
|
2 |
+
import os, re, sys, bpy, time, bmesh, mathutils
|
3 |
+
from . import common
|
4 |
+
|
5 |
+
# メニュー等に項目追加
|
6 |
+
def menu_func(self, context):
|
7 |
+
mate = context.material
|
8 |
+
if not mate:
|
9 |
+
col = self.layout.column(align=True)
|
10 |
+
col.operator('material.new_cm3d2', icon_value=common.preview_collections['main']['KISS'].icon_id)
|
11 |
+
row = col.row(align=True)
|
12 |
+
row.operator('material.import_cm3d2_mate', icon='FILE_FOLDER', text="Import .Mate")
|
13 |
+
row.operator('material.paste_material', icon='PASTEDOWN', text="Paste")
|
14 |
+
else:
|
15 |
+
if 'shader1' in mate and 'shader2' in mate:
|
16 |
+
box = self.layout.box()
|
17 |
+
#row = box.split(percentage=0.3)
|
18 |
+
row = box.split(percentage=0.5)
|
19 |
+
row.label(text="CM3D2 Material", icon_value=common.preview_collections['main']['KISS'].icon_id)
|
20 |
+
sub_row = row.row(align=True)
|
21 |
+
sub_row.operator('material.export_cm3d2_mate', icon='FILE_FOLDER', text="Export .Mate")
|
22 |
+
sub_row.operator('material.copy_material', icon='COPYDOWN', text="Copy")
|
23 |
+
sub_row.operator('material.paste_material2', icon='PASTEDOWN', text="Paste")
|
24 |
+
|
25 |
+
type_name = "Unknown"
|
26 |
+
icon = 'ERROR'
|
27 |
+
if mate['shader1'] == 'CM3D2/Toony_Lighted':
|
28 |
+
type_name = "Toony"
|
29 |
+
icon = 'SOLID'
|
30 |
+
elif mate['shader1'] == 'CM3D2/Toony_Lighted_Hair':
|
31 |
+
type_name = "Toony Hair"
|
32 |
+
icon = 'PARTICLEMODE'
|
33 |
+
elif mate['shader1'] == 'CM3D2/Toony_Lighted_Trans':
|
34 |
+
type_name = "Toony Lighted Trans"
|
35 |
+
icon = 'WIRE'
|
36 |
+
elif mate['shader1'] == 'CM3D2/Toony_Lighted_Trans_NoZ':
|
37 |
+
type_name = "Toony Lighted Trans NoZ"
|
38 |
+
icon = 'DRIVER'
|
39 |
+
elif mate['shader1'] == 'CM3D2/Toony_Lighted_Outline':
|
40 |
+
type_name = "Toony Lighted Outline"
|
41 |
+
icon = 'ANTIALIASED'
|
42 |
+
elif mate['shader1'] == 'CM3D2/Toony_Lighted_Hair_Outline':
|
43 |
+
type_name = "Toony Light Hair Outline"
|
44 |
+
icon = 'PARTICLEMODE'
|
45 |
+
elif mate['shader1'] == 'CM3D2/Toony_Lighted_Outline_Trans':
|
46 |
+
type_name = "Toony Lighted Outline Trans"
|
47 |
+
icon = 'PROP_OFF'
|
48 |
+
elif mate['shader1'] == 'CM3D2/Lighted_Trans':
|
49 |
+
type_name = "Lighted Trans"
|
50 |
+
icon = 'VISIBLE_IPO_OFF'
|
51 |
+
elif mate['shader1'] == 'CM3D2/Lighted':
|
52 |
+
type_name = "Lighted"
|
53 |
+
icon = 'MATSPHERE'
|
54 |
+
elif mate['shader1'] == 'Unlit/Texture':
|
55 |
+
type_name = "Unlit Texture"
|
56 |
+
icon = 'PARTICLES'
|
57 |
+
elif mate['shader1'] == 'Unlit/Transparent':
|
58 |
+
type_name = "Unlit Trans"
|
59 |
+
icon = 'MOD_PARTICLES'
|
60 |
+
elif mate['shader1'] == 'CM3D2/Mosaic':
|
61 |
+
type_name = "Mosiac"
|
62 |
+
icon = 'ALIASED'
|
63 |
+
elif mate['shader1'] == 'CM3D2/Man':
|
64 |
+
type_name = "Man"
|
65 |
+
icon = 'ARMATURE_DATA'
|
66 |
+
elif mate['shader1'] == 'Diffuse':
|
67 |
+
type_name = "Diffuse"
|
68 |
+
icon = 'BRUSH_CLAY_STRIPS'
|
69 |
+
elif mate['shader1'] == 'Transparent/Diffuse':
|
70 |
+
type_name = "Transparent Diffuse"
|
71 |
+
icon = 'MATCAP_09'
|
72 |
+
elif mate['shader1'] == 'CM3D2_Debug/Debug_CM3D2_Normal2Color':
|
73 |
+
type_name = "Debug"
|
74 |
+
icon = 'MATCAP_23'
|
75 |
+
|
76 |
+
row = box.split(percentage=0.333333333333333333333)
|
77 |
+
row.label(text="Type:")
|
78 |
+
row.label(text=type_name, icon=icon)
|
79 |
+
box.prop(mate, 'name', icon='SORTALPHA', text="Material Name")
|
80 |
+
box.prop(mate, '["shader1"]', icon='MATERIAL', text="Shader 1")
|
81 |
+
box.prop(mate, '["shader2"]', icon='SMOOTH', text="Shader 2")
|
82 |
+
|
83 |
+
box.operator('material.decorate_material', icon='TEXTURE_SHADED')
|
84 |
+
|
85 |
+
|
86 |
+
if 'CM3D2 Texture Expand' not in mate:
|
87 |
+
mate['CM3D2 Texture Expand'] = True
|
88 |
+
box = self.layout.box()
|
89 |
+
if mate['CM3D2 Texture Expand']:
|
90 |
+
|
91 |
+
row = box.row()
|
92 |
+
row.alignment = 'LEFT'
|
93 |
+
op = row.operator('wm.context_set_int', icon='DOWNARROW_HLT', text="", emboss=False)
|
94 |
+
op.data_path, op.value, op.relative = 'material["CM3D2 Texture Expand"]', 0, False
|
95 |
+
row.label(text="Texture Info", icon_value=common.preview_collections['main']['KISS'].icon_id)
|
96 |
+
|
97 |
+
for slot in mate.texture_slots:
|
98 |
+
if not slot: continue
|
99 |
+
if not slot.texture: continue
|
100 |
+
tex = slot.texture
|
101 |
+
name = common.remove_serial_number(tex.name).replace("_", "") + " "
|
102 |
+
|
103 |
+
if slot.use: type = 'tex'
|
104 |
+
else: type = 'col' if slot.use_rgb_to_intensity else 'f'
|
105 |
+
|
106 |
+
if type == 'tex':
|
107 |
+
row = box.row(align=True)
|
108 |
+
sub_row = row.split(percentage=0.333333333333333333333, align=True)
|
109 |
+
sub_row.label(text=name, icon_value=sub_row.icon(tex))
|
110 |
+
if 'image' in dir(tex):
|
111 |
+
if tex.image:
|
112 |
+
sub_row.template_ID(tex, 'image')
|
113 |
+
row.operator('material.quick_texture_show', text="", icon='RIGHTARROW').texture_name = tex.name
|
114 |
+
elif type == 'col':
|
115 |
+
row = box.row(align=True)
|
116 |
+
sub_row = row.split(percentage=0.333333333333333333333, align=True)
|
117 |
+
sub_row.label(text=name, icon_value=sub_row.icon(tex))
|
118 |
+
sub_row.prop(slot, 'color', text="")
|
119 |
+
sub_row.prop(slot, 'diffuse_color_factor', icon='IMAGE_RGB_ALPHA', text="Alpha", slider=True)
|
120 |
+
row.operator('material.quick_texture_show', text="", icon='RIGHTARROW').texture_name = tex.name
|
121 |
+
elif type == 'f':
|
122 |
+
row = box.row(align=True)
|
123 |
+
sub_row = row.split(percentage=0.333333333333333333333, align=True)
|
124 |
+
sub_row.label(text=name, icon_value=sub_row.icon(tex))
|
125 |
+
sub_row.prop(slot, 'diffuse_color_factor', icon='ARROW_LEFTRIGHT', text="Value")
|
126 |
+
row.operator('material.quick_texture_show', text="", icon='RIGHTARROW').texture_name = tex.name
|
127 |
+
|
128 |
+
box.operator('texture.sync_tex_color_ramps', icon='LINKED')
|
129 |
+
|
130 |
+
else:
|
131 |
+
row = box.row()
|
132 |
+
row.alignment = 'LEFT'
|
133 |
+
op = row.operator('wm.context_set_int', icon='RIGHTARROW', text="", emboss=False)
|
134 |
+
op.data_path, op.value, op.relative = 'material["CM3D2 Texture Expand"]', 1, False
|
135 |
+
row.label(text="Texture Info", icon_value=common.preview_collections['main']['KISS'].icon_id)
|
136 |
+
|
137 |
+
else:
|
138 |
+
self.layout.operator('material.new_cm3d2', text="Create CM3D2 Material", icon_value=common.preview_collections['main']['KISS'].icon_id)
|
139 |
+
|
140 |
+
class new_cm3d2(bpy.types.Operator):
|
141 |
+
bl_idname = 'material.new_cm3d2'
|
142 |
+
bl_label = "Create a Material for CM3D2"
|
143 |
+
bl_description = "Blender-CM3D2-Create a material that can be exported."
|
144 |
+
bl_options = {'REGISTER', 'UNDO'}
|
145 |
+
|
146 |
+
items = [
|
147 |
+
('CM3D2/Toony_Lighted', "Toony", "", 'SOLID', 0),
|
148 |
+
('CM3D2/Toony_Lighted_Hair', "Toony Lighted Hair", "", 'PARTICLEMODE', 1),
|
149 |
+
('CM3D2/Toony_Lighted_Trans', "Toony Lighted Trans", "", 'WIRE', 2),
|
150 |
+
('CM3D2/Toony_Lighted_Trans_NoZ', "Toony Lighted Trans NoZ", "", 'DRIVER', 3),
|
151 |
+
('CM3D2/Toony_Lighted_Outline', "Toony Lighted Outline", "", 'ANTIALIASED', 4),
|
152 |
+
('CM3D2/Toony_Lighted_Hair_Outline', "Toony Lighted Hair Outline", "", 'PARTICLEMODE', 5),
|
153 |
+
('CM3D2/Toony_Lighted_Outline_Trans', "Toony Lighted Outline Trans", "", 'PROP_OFF', 6),
|
154 |
+
('CM3D2/Lighted_Trans', "Lighted Trans", "", 'VISIBLE_IPO_OFF', 7),
|
155 |
+
('CM3D2/Lighted', "Lighted", "", 'MATSPHERE', 8),
|
156 |
+
('Unlit/Texture', "Unlit/Texture", "", 'PARTICLES', 9),
|
157 |
+
('Unlit/Transparent', "Unlit/Trans", "", 'MOD_PARTICLES', 10),
|
158 |
+
('CM3D2/Mosaic', "Mosiac", "", 'ALIASED', 11),
|
159 |
+
('CM3D2/Man', "Man", "", 'ARMATURE_DATA', 12),
|
160 |
+
('Diffuse', "Diffuse", "", 'BRUSH_CLAY_STRIPS', 13),
|
161 |
+
('Transparent/Diffuse', "Trans Diffuse", "", 'MATCAP_09', 14),
|
162 |
+
('CM3D2_Debug/Debug_CM3D2_Normal2Color', "Debug", "", 'MATCAP_23', 15),
|
163 |
+
]
|
164 |
+
type = bpy.props.EnumProperty(items=items, name="Shaders:", default='CM3D2/Toony_Lighted_Outline')
|
165 |
+
is_decorate = bpy.props.BoolProperty(name="Decorate Material", default=True)
|
166 |
+
is_replace_cm3d2_tex = bpy.props.BoolProperty(name="Find Textures", default=False, description="Will Search for any corresponding textures and will apply them.")
|
167 |
+
|
168 |
+
@classmethod
|
169 |
+
def poll(cls, context):
|
170 |
+
return True
|
171 |
+
|
172 |
+
def invoke(self, context, event):
|
173 |
+
self.is_replace_cm3d2_tex = common.preferences().is_replace_cm3d2_tex
|
174 |
+
return context.window_manager.invoke_props_dialog(self)
|
175 |
+
|
176 |
+
def draw(self, context):
|
177 |
+
self.layout.separator()
|
178 |
+
self.layout.prop(self, 'type', icon='MATERIAL')
|
179 |
+
self.layout.prop(self, 'is_decorate', icon='TEXTURE_SHADED')
|
180 |
+
self.layout.prop(self, 'is_replace_cm3d2_tex', icon='BORDERMOVE')
|
181 |
+
|
182 |
+
def execute(self, context):
|
183 |
+
ob = context.active_object
|
184 |
+
me = ob.data
|
185 |
+
ob_names = common.remove_serial_number(ob.name).split('.')
|
186 |
+
ob_name = ob_names[0]
|
187 |
+
|
188 |
+
if context.material:
|
189 |
+
mate = context.material
|
190 |
+
for index, slot in enumerate(mate.texture_slots):
|
191 |
+
mate.texture_slots.clear(index)
|
192 |
+
else:
|
193 |
+
if not context.material_slot:
|
194 |
+
bpy.ops.object.material_slot_add()
|
195 |
+
mate = context.blend_data.materials.new(ob_name)
|
196 |
+
|
197 |
+
context.material_slot.material = mate
|
198 |
+
tex_list, col_list, f_list = [], [], []
|
199 |
+
|
200 |
+
base_path = "Assets\\texture\\texture\\"
|
201 |
+
pref = common.preferences()
|
202 |
+
|
203 |
+
_MainTex = ("_MainTex", ob_name, base_path + ob_name + ".png")
|
204 |
+
_ToonRamp = ("_ToonRamp", pref.new_mate_toonramp_name, pref.new_mate_toonramp_path)
|
205 |
+
_ShadowTex = ("_ShadowTex", ob_name + "_shadow", base_path + ob_name + "_shadow.png")
|
206 |
+
_ShadowRateToon = ("_ShadowRateToon", pref.new_mate_shadowratetoon_name, pref.new_mate_shadowratetoon_path)
|
207 |
+
_HiTex = ("_HiTex", ob_name + "_s", base_path + ob_name + "_s.png")
|
208 |
+
|
209 |
+
_Color = ("_Color", pref.new_mate_color)
|
210 |
+
_ShadowColor = ("_ShadowColor", pref.new_mate_shadowcolor)
|
211 |
+
_RimColor = ("_RimColor", pref.new_mate_rimcolor)
|
212 |
+
_OutlineColor = ("_OutlineColor", pref.new_mate_outlinecolor)
|
213 |
+
|
214 |
+
_Shininess = ("_Shininess", pref.new_mate_shininess)
|
215 |
+
_OutlineWidth = ("_OutlineWidth", pref.new_mate_outlinewidth)
|
216 |
+
_RimPower = ("_RimPower", pref.new_mate_rimpower)
|
217 |
+
_RimShift = ("_RimShift", pref.new_mate_rimshift)
|
218 |
+
_HiRate = ("_HiRate", pref.new_mate_hirate)
|
219 |
+
_HiPow = ("_HiPow", pref.new_mate_hipow)
|
220 |
+
|
221 |
+
if False:
|
222 |
+
pass
|
223 |
+
elif self.type == 'CM3D2/Toony_Lighted_Outline':
|
224 |
+
mate['shader1'] = 'CM3D2/Toony_Lighted_Outline'
|
225 |
+
mate['shader2'] = 'CM3D2__Toony_Lighted_Outline'
|
226 |
+
tex_list.append(_MainTex)
|
227 |
+
tex_list.append(_ToonRamp)
|
228 |
+
tex_list.append(_ShadowTex)
|
229 |
+
tex_list.append(_ShadowRateToon)
|
230 |
+
col_list.append(_Color)
|
231 |
+
col_list.append(_ShadowColor)
|
232 |
+
col_list.append(_RimColor)
|
233 |
+
col_list.append(_OutlineColor)
|
234 |
+
f_list.append(_Shininess)
|
235 |
+
f_list.append(_OutlineWidth)
|
236 |
+
f_list.append(_RimPower)
|
237 |
+
f_list.append(_RimShift)
|
238 |
+
elif self.type == 'CM3D2/Toony_Lighted_Trans':
|
239 |
+
mate['shader1'] = 'CM3D2/Toony_Lighted_Trans'
|
240 |
+
mate['shader2'] = 'CM3D2__Toony_Lighted_Trans'
|
241 |
+
tex_list.append(_MainTex)
|
242 |
+
tex_list.append(_ToonRamp)
|
243 |
+
tex_list.append(_ShadowTex)
|
244 |
+
tex_list.append(_ShadowRateToon)
|
245 |
+
col_list.append(_Color)
|
246 |
+
col_list.append(_ShadowColor)
|
247 |
+
col_list.append(_RimColor)
|
248 |
+
f_list.append(_Shininess)
|
249 |
+
f_list.append(_RimPower)
|
250 |
+
f_list.append(_RimShift)
|
251 |
+
elif self.type == 'CM3D2/Toony_Lighted_Hair_Outline':
|
252 |
+
mate['shader1'] = 'CM3D2/Toony_Lighted_Hair_Outline'
|
253 |
+
mate['shader2'] = 'CM3D2__Toony_Lighted_Hair_Outline'
|
254 |
+
tex_list.append(_MainTex)
|
255 |
+
tex_list.append(_ToonRamp)
|
256 |
+
tex_list.append(_ShadowTex)
|
257 |
+
tex_list.append(_ShadowRateToon)
|
258 |
+
tex_list.append(_HiTex)
|
259 |
+
col_list.append(_Color)
|
260 |
+
col_list.append(_ShadowColor)
|
261 |
+
col_list.append(_RimColor)
|
262 |
+
col_list.append(_OutlineColor)
|
263 |
+
f_list.append(_Shininess)
|
264 |
+
f_list.append(_OutlineWidth)
|
265 |
+
f_list.append(_RimPower)
|
266 |
+
f_list.append(_RimShift)
|
267 |
+
f_list.append(_HiRate)
|
268 |
+
f_list.append(_HiPow)
|
269 |
+
elif self.type == 'CM3D2/Mosaic':
|
270 |
+
mate['shader1'] = 'CM3D2/Mosaic'
|
271 |
+
mate['shader2'] = 'CM3D2__Mosaic'
|
272 |
+
tex_list.append(("_RenderTex", ""))
|
273 |
+
f_list.append(("_FloatValue1", 30))
|
274 |
+
elif self.type == 'Unlit/Texture':
|
275 |
+
mate['shader1'] = 'Unlit/Texture'
|
276 |
+
mate['shader2'] = 'Unlit__Texture'
|
277 |
+
tex_list.append(_MainTex)
|
278 |
+
col_list.append(_Color)
|
279 |
+
elif self.type == 'Unlit/Transparent':
|
280 |
+
mate['shader1'] = 'Unlit/Transparent'
|
281 |
+
mate['shader2'] = 'Unlit__Transparent'
|
282 |
+
tex_list.append(_MainTex)
|
283 |
+
col_list.append(_Color)
|
284 |
+
col_list.append(_ShadowColor)
|
285 |
+
col_list.append(_RimColor)
|
286 |
+
f_list.append(_Shininess)
|
287 |
+
f_list.append(_RimPower)
|
288 |
+
f_list.append(_RimShift)
|
289 |
+
elif self.type == 'CM3D2/Man':
|
290 |
+
mate['shader1'] = 'CM3D2/Man'
|
291 |
+
mate['shader2'] = 'CM3D2__Man'
|
292 |
+
col_list.append(_Color)
|
293 |
+
f_list.append(("_FloatValue2", 0.5))
|
294 |
+
f_list.append(("_FloatValue3", 1))
|
295 |
+
elif self.type == 'Diffuse':
|
296 |
+
mate['shader1'] = 'Diffuse'
|
297 |
+
mate['shader2'] = 'Diffuse'
|
298 |
+
tex_list.append(_MainTex)
|
299 |
+
col_list.append(_Color)
|
300 |
+
elif self.type == 'CM3D2/Toony_Lighted_Trans_NoZ':
|
301 |
+
mate['shader1'] = 'CM3D2/Toony_Lighted_Trans_NoZ'
|
302 |
+
mate['shader2'] = 'CM3D2__Toony_Lighted_Trans_NoZ'
|
303 |
+
tex_list.append(_MainTex)
|
304 |
+
tex_list.append(_ToonRamp)
|
305 |
+
tex_list.append(_ShadowTex)
|
306 |
+
tex_list.append(_ShadowRateToon)
|
307 |
+
col_list.append(_Color)
|
308 |
+
col_list.append(_ShadowColor)
|
309 |
+
col_list.append(_RimColor)
|
310 |
+
col_list.append(_OutlineColor)
|
311 |
+
f_list.append(_Shininess)
|
312 |
+
f_list.append(_OutlineWidth)
|
313 |
+
f_list.append(_RimPower)
|
314 |
+
f_list.append(_RimShift)
|
315 |
+
elif self.type == 'CM3D2/Toony_Lighted_Outline_Trans':
|
316 |
+
mate['shader1'] = 'CM3D2/Toony_Lighted_Outline_Trans'
|
317 |
+
mate['shader2'] = 'CM3D2__Toony_Lighted_Outline_Trans'
|
318 |
+
tex_list.append(_MainTex)
|
319 |
+
tex_list.append(_ToonRamp)
|
320 |
+
tex_list.append(_ShadowTex)
|
321 |
+
tex_list.append(_ShadowRateToon)
|
322 |
+
col_list.append(_Color)
|
323 |
+
col_list.append(_ShadowColor)
|
324 |
+
col_list.append(_RimColor)
|
325 |
+
col_list.append(_OutlineColor)
|
326 |
+
f_list.append(_Shininess)
|
327 |
+
f_list.append(_OutlineWidth)
|
328 |
+
f_list.append(_RimPower)
|
329 |
+
f_list.append(_RimShift)
|
330 |
+
elif self.type == 'CM3D2/Lighted_Trans':
|
331 |
+
mate['shader1'] = 'CM3D2/Lighted_Trans'
|
332 |
+
mate['shader2'] = 'CM3D2__Lighted_Trans'
|
333 |
+
tex_list.append(_MainTex)
|
334 |
+
col_list.append(_Color)
|
335 |
+
col_list.append(_ShadowColor)
|
336 |
+
f_list.append(_Shininess)
|
337 |
+
elif self.type == 'CM3D2/Lighted':
|
338 |
+
mate['shader1'] = 'CM3D2/Lighted'
|
339 |
+
mate['shader2'] = 'CM3D2__Lighted'
|
340 |
+
tex_list.append(_MainTex)
|
341 |
+
col_list.append(_Color)
|
342 |
+
col_list.append(_ShadowColor)
|
343 |
+
f_list.append(_Shininess)
|
344 |
+
elif self.type == 'CM3D2/Toony_Lighted':
|
345 |
+
mate['shader1'] = 'CM3D2/Toony_Lighted'
|
346 |
+
mate['shader2'] = 'CM3D2__Toony_Lighted'
|
347 |
+
tex_list.append(_MainTex)
|
348 |
+
tex_list.append(_ToonRamp)
|
349 |
+
tex_list.append(_ShadowTex)
|
350 |
+
tex_list.append(_ShadowRateToon)
|
351 |
+
col_list.append(_Color)
|
352 |
+
col_list.append(_ShadowColor)
|
353 |
+
col_list.append(_RimColor)
|
354 |
+
f_list.append(_Shininess)
|
355 |
+
f_list.append(_RimPower)
|
356 |
+
f_list.append(_RimShift)
|
357 |
+
elif self.type == 'CM3D2/Toony_Lighted_Hair':
|
358 |
+
mate['shader1'] = 'CM3D2/Toony_Lighted_Hair_Outline'
|
359 |
+
mate['shader2'] = 'CM3D2__Toony_Lighted_Hair_Outline'
|
360 |
+
tex_list.append(_MainTex)
|
361 |
+
tex_list.append(_ToonRamp)
|
362 |
+
tex_list.append(_ShadowTex)
|
363 |
+
tex_list.append(_ShadowRateToon)
|
364 |
+
tex_list.append(_HiTex)
|
365 |
+
col_list.append(_Color)
|
366 |
+
col_list.append(_ShadowColor)
|
367 |
+
col_list.append(_RimColor)
|
368 |
+
f_list.append(_Shininess)
|
369 |
+
f_list.append(_RimPower)
|
370 |
+
f_list.append(_RimShift)
|
371 |
+
f_list.append(_HiRate)
|
372 |
+
f_list.append(_HiPow)
|
373 |
+
elif self.type == 'Transparent/Diffuse':
|
374 |
+
mate['shader1'] = 'Transparent/Diffuse'
|
375 |
+
mate['shader2'] = 'Transparent__Diffuse'
|
376 |
+
tex_list.append(_MainTex)
|
377 |
+
col_list.append(_Color)
|
378 |
+
col_list.append(_ShadowColor)
|
379 |
+
col_list.append(_RimColor)
|
380 |
+
col_list.append(_OutlineColor)
|
381 |
+
f_list.append(_Shininess)
|
382 |
+
f_list.append(_OutlineWidth)
|
383 |
+
f_list.append(_RimPower)
|
384 |
+
f_list.append(_RimShift)
|
385 |
+
elif self.type == 'CM3D2_Debug/Debug_CM3D2_Normal2Color':
|
386 |
+
mate['shader1'] = 'CM3D2_Debug/Debug_CM3D2_Normal2Color'
|
387 |
+
mate['shader2'] = 'CM3D2_Debug__Debug_CM3D2_Normal2Color'
|
388 |
+
col_list.append(_Color)
|
389 |
+
col_list.append(_RimColor)
|
390 |
+
col_list.append(_OutlineColor)
|
391 |
+
col_list.append(("_SpecColor", (1, 1, 1, 1)))
|
392 |
+
f_list.append(_Shininess)
|
393 |
+
f_list.append(_OutlineWidth)
|
394 |
+
f_list.append(_RimPower)
|
395 |
+
f_list.append(_RimShift)
|
396 |
+
|
397 |
+
tex_storage_files = common.get_tex_storage_files()
|
398 |
+
slot_count = 0
|
399 |
+
for data in tex_list:
|
400 |
+
slot = mate.texture_slots.create(slot_count)
|
401 |
+
tex = context.blend_data.textures.new(data[0], 'IMAGE')
|
402 |
+
slot.texture = tex
|
403 |
+
if data[1] == "":
|
404 |
+
slot_count += 1
|
405 |
+
continue
|
406 |
+
slot.color = pref.new_mate_tex_color[:3]
|
407 |
+
slot.diffuse_color_factor = pref.new_mate_tex_color[3]
|
408 |
+
img = context.blend_data.images.new(data[1], 128, 128)
|
409 |
+
img.filepath = data[2]
|
410 |
+
img['cm3d2_path'] = data[2]
|
411 |
+
img.source = 'FILE'
|
412 |
+
tex.image = img
|
413 |
+
slot_count += 1
|
414 |
+
|
415 |
+
# tex探し
|
416 |
+
if self.is_replace_cm3d2_tex:
|
417 |
+
if common.replace_cm3d2_tex(img, tex_storage_files) and data[0]=='_MainTex':
|
418 |
+
for face in me.polygons:
|
419 |
+
if face.material_index == ob.active_material_index:
|
420 |
+
me.uv_textures.active.data[face.index].image = img
|
421 |
+
|
422 |
+
for data in col_list:
|
423 |
+
slot = mate.texture_slots.create(slot_count)
|
424 |
+
mate.use_textures[slot_count] = False
|
425 |
+
slot.color = data[1][:3]
|
426 |
+
slot.diffuse_color_factor = data[1][3]
|
427 |
+
slot.use_rgb_to_intensity = True
|
428 |
+
tex = context.blend_data.textures.new(data[0], 'BLEND')
|
429 |
+
slot.texture = tex
|
430 |
+
slot_count += 1
|
431 |
+
|
432 |
+
for data in f_list:
|
433 |
+
slot = mate.texture_slots.create(slot_count)
|
434 |
+
mate.use_textures[slot_count] = False
|
435 |
+
slot.diffuse_color_factor = data[1]
|
436 |
+
tex = context.blend_data.textures.new(data[0], 'BLEND')
|
437 |
+
slot.texture = tex
|
438 |
+
slot_count += 1
|
439 |
+
|
440 |
+
common.decorate_material(mate, self.is_decorate, me, ob.active_material_index)
|
441 |
+
return {'FINISHED'}
|
442 |
+
|
443 |
+
class paste_material(bpy.types.Operator):
|
444 |
+
bl_idname = 'material.paste_material'
|
445 |
+
bl_label = "Paste Material"
|
446 |
+
bl_description = "Create a new material from the material data in the clipboard."
|
447 |
+
bl_options = {'REGISTER', 'UNDO'}
|
448 |
+
|
449 |
+
is_decorate = bpy.props.BoolProperty(name="Decorate Material", default=True)
|
450 |
+
is_replace_cm3d2_tex = bpy.props.BoolProperty(name="Find texture", default=False, description="Will Search for any corresponding textures and will apply them")
|
451 |
+
|
452 |
+
@classmethod
|
453 |
+
def poll(cls, context):
|
454 |
+
data = context.window_manager.clipboard
|
455 |
+
lines = data.split('\n')
|
456 |
+
if len(lines) < 10:
|
457 |
+
return False
|
458 |
+
match_strs = ['\ntex\n', '\ncol\n', '\nf\n', '\n\t_MainTex\n', '\n\t_Color\n', '\n\t_Shininess\n']
|
459 |
+
for s in match_strs:
|
460 |
+
if s in data:
|
461 |
+
return True
|
462 |
+
return False
|
463 |
+
|
464 |
+
def invoke(self, context, event):
|
465 |
+
self.is_replace_cm3d2_tex = common.preferences().is_replace_cm3d2_tex
|
466 |
+
return context.window_manager.invoke_props_dialog(self)
|
467 |
+
|
468 |
+
def draw(self, context):
|
469 |
+
self.layout.prop(self, 'is_decorate')
|
470 |
+
self.layout.prop(self, 'is_replace_cm3d2_tex', icon='BORDERMOVE')
|
471 |
+
|
472 |
+
def execute(self, context):
|
473 |
+
data = context.window_manager.clipboard
|
474 |
+
lines = data.split('\n')
|
475 |
+
|
476 |
+
ob = context.active_object
|
477 |
+
me = ob.data
|
478 |
+
|
479 |
+
if not context.material_slot:
|
480 |
+
bpy.ops.object.material_slot_add()
|
481 |
+
mate = context.blend_data.materials.new(lines[2])
|
482 |
+
context.material_slot.material = mate
|
483 |
+
|
484 |
+
mate['shader1'] = lines[3]
|
485 |
+
mate['shader2'] = lines[4]
|
486 |
+
|
487 |
+
slot_index = 0
|
488 |
+
line_seek = 5
|
489 |
+
for i in range(99999):
|
490 |
+
if len(lines) <= line_seek:
|
491 |
+
break
|
492 |
+
type = common.line_trim(lines[line_seek])
|
493 |
+
if not type:
|
494 |
+
line_seek += 1
|
495 |
+
continue
|
496 |
+
if type == 'tex':
|
497 |
+
slot = mate.texture_slots.create(slot_index)
|
498 |
+
tex = context.blend_data.textures.new(common.line_trim(lines[line_seek+1]), 'IMAGE')
|
499 |
+
slot.texture = tex
|
500 |
+
sub_type = common.line_trim(lines[line_seek+2])
|
501 |
+
line_seek += 3
|
502 |
+
if sub_type == 'tex2d':
|
503 |
+
img = context.blend_data.images.new(common.line_trim(lines[line_seek]), 128, 128)
|
504 |
+
img['cm3d2_path'] = common.line_trim(lines[line_seek+1])
|
505 |
+
img.filepath = img['cm3d2_path']
|
506 |
+
img.source = 'FILE'
|
507 |
+
tex.image = img
|
508 |
+
fs = common.line_trim(lines[line_seek+2]).split(' ')
|
509 |
+
for fi in range(len(fs)):
|
510 |
+
fs[fi] = float(fs[fi])
|
511 |
+
slot.color = fs[:3]
|
512 |
+
slot.diffuse_color_factor = fs[3]
|
513 |
+
line_seek += 3
|
514 |
+
|
515 |
+
# tex探し
|
516 |
+
if self.is_replace_cm3d2_tex:
|
517 |
+
if common.replace_cm3d2_tex(img) and data[0]=='_MainTex':
|
518 |
+
for face in me.polygons:
|
519 |
+
if face.material_index == ob.active_material_index:
|
520 |
+
me.uv_textures.active.data[face.index].image = img
|
521 |
+
|
522 |
+
elif type == 'col':
|
523 |
+
slot = mate.texture_slots.create(slot_index)
|
524 |
+
tex_name = common.line_trim(lines[line_seek+1])
|
525 |
+
tex = context.blend_data.textures.new(tex_name, 'BLEND')
|
526 |
+
mate.use_textures[slot_index] = False
|
527 |
+
slot.use_rgb_to_intensity = True
|
528 |
+
fs = common.line_trim(lines[line_seek+2]).split(' ')
|
529 |
+
for fi in range(len(fs)):
|
530 |
+
fs[fi] = float(fs[fi])
|
531 |
+
slot.color = fs[:3]
|
532 |
+
slot.diffuse_color_factor = fs[3]
|
533 |
+
slot.texture = tex
|
534 |
+
line_seek += 3
|
535 |
+
|
536 |
+
elif type == 'f':
|
537 |
+
slot = mate.texture_slots.create(slot_index)
|
538 |
+
tex_name = common.line_trim(lines[line_seek+1])
|
539 |
+
tex = context.blend_data.textures.new(tex_name, 'BLEND')
|
540 |
+
mate.use_textures[slot_index] = False
|
541 |
+
slot.diffuse_color_factor = float(common.line_trim(lines[line_seek+2]))
|
542 |
+
slot.texture = tex
|
543 |
+
line_seek += 3
|
544 |
+
|
545 |
+
else:
|
546 |
+
self.report(type={'ERROR'}, message="Unknown value found, mission aborted.")
|
547 |
+
return {'CANCELLED'}
|
548 |
+
slot_index += 1
|
549 |
+
|
550 |
+
common.decorate_material(mate, self.is_decorate, me, ob.active_material_index)
|
551 |
+
self.report(type={'INFO'}, message="Material Pasted.")
|
552 |
+
return {'FINISHED'}
|
553 |
+
|
554 |
+
class paste_material2(bpy.types.Operator):
|
555 |
+
bl_idname = 'material.paste_material2'
|
556 |
+
bl_label = "Paste Material"
|
557 |
+
bl_description = "Material data will be pasted from the clipboard."
|
558 |
+
bl_options = {'REGISTER', 'UNDO'}
|
559 |
+
|
560 |
+
@classmethod
|
561 |
+
def poll(cls, context):
|
562 |
+
data = context.window_manager.clipboard
|
563 |
+
lines = data.split('\n')
|
564 |
+
if len(lines) < 10:
|
565 |
+
return False
|
566 |
+
match_strs = ['\ntex\n', '\ncol\n', '\nf\n', '\n\t_MainTex\n', '\n\t_Color\n', '\n\t_Shininess\n']
|
567 |
+
for s in match_strs:
|
568 |
+
if s in data:
|
569 |
+
return True
|
570 |
+
return False
|
571 |
+
|
572 |
+
def execute(self, context):
|
573 |
+
data = context.window_manager.clipboard
|
574 |
+
lines = data.split('\n')
|
575 |
+
|
576 |
+
ob = context.active_object
|
577 |
+
me = ob.data
|
578 |
+
|
579 |
+
mate = context.material
|
580 |
+
# シリアル番号が異なる場合は変更しない
|
581 |
+
if common.remove_serial_number(lines[2]) != common.remove_serial_number(mate.name):
|
582 |
+
mate.name = lines[2]
|
583 |
+
mate['shader1'] = lines[3]
|
584 |
+
mate['shader2'] = lines[4]
|
585 |
+
|
586 |
+
slot_index = 0
|
587 |
+
line_seek = 5
|
588 |
+
olds_slots = {}
|
589 |
+
|
590 |
+
def search_or_create_slot(slot_index, prop_name, tex_type):
|
591 |
+
tex = None
|
592 |
+
slot_item = mate.texture_slots[slot_index]
|
593 |
+
name = slot_item.name if slot_item else ''
|
594 |
+
|
595 |
+
slot_item_name = common.remove_serial_number(name)
|
596 |
+
if prop_name == slot_item_name:
|
597 |
+
slot = slot_item
|
598 |
+
tex = slot_item.texture
|
599 |
+
else:
|
600 |
+
if slot_item: olds_slots[slot_item_name] = slot_item
|
601 |
+
slot = mate.texture_slots.create(slot_index)
|
602 |
+
|
603 |
+
if prop_name in olds_slots:
|
604 |
+
tex = olds_slots.pop(prop_name).texture
|
605 |
+
else:
|
606 |
+
for item_index in range(slot_index+1, len(mate.texture_slots)):
|
607 |
+
slot_item = mate.texture_slots[item_index]
|
608 |
+
if slot_item is None: break
|
609 |
+
if prop_name == common.remove_serial_number(slot_item.name):
|
610 |
+
tex = slot_item.texture
|
611 |
+
break
|
612 |
+
if tex is None:
|
613 |
+
tex = context.blend_data.textures.new(prop_name, tex_type)
|
614 |
+
slot.texture = tex
|
615 |
+
return slot
|
616 |
+
|
617 |
+
for i in range(99999):
|
618 |
+
if len(lines) <= line_seek:
|
619 |
+
break
|
620 |
+
type = common.line_trim(lines[line_seek])
|
621 |
+
if not type:
|
622 |
+
line_seek += 1
|
623 |
+
continue
|
624 |
+
if type == 'tex':
|
625 |
+
prop_name = common.line_trim(lines[line_seek+1])
|
626 |
+
sub_type = common.line_trim(lines[line_seek+2])
|
627 |
+
|
628 |
+
slot = search_or_create_slot(slot_index, prop_name, 'IMAGE')
|
629 |
+
slot.use_rgb_to_intensity = False
|
630 |
+
mate.use_textures[slot_index] = True
|
631 |
+
|
632 |
+
line_seek += 3
|
633 |
+
if sub_type == 'tex2d':
|
634 |
+
tex = slot.texture
|
635 |
+
tex_name = common.line_trim(lines[line_seek])
|
636 |
+
tex_path = common.line_trim(lines[line_seek+1])
|
637 |
+
if tex.image is None:
|
638 |
+
img = context.blend_data.images.new(tex_name, 128, 128)
|
639 |
+
img.source = 'FILE'
|
640 |
+
tex.image = img
|
641 |
+
|
642 |
+
# シリアル番号を残す
|
643 |
+
if tex_name != common.remove_serial_number(tex.image.name):
|
644 |
+
tex.image.name = tex_name
|
645 |
+
tex.image['cm3d2_path'] = tex_path
|
646 |
+
tex.image.filepath = tex.image['cm3d2_path']
|
647 |
+
|
648 |
+
fs = common.line_trim(lines[line_seek+2]).split(' ')
|
649 |
+
for fi in range(len(fs)):
|
650 |
+
fs[fi] = float(fs[fi])
|
651 |
+
slot.color = fs[:3]
|
652 |
+
slot.diffuse_color_factor = fs[3]
|
653 |
+
line_seek += 3
|
654 |
+
|
655 |
+
elif type == 'col':
|
656 |
+
prop_name = common.line_trim(lines[line_seek+1])
|
657 |
+
|
658 |
+
slot = search_or_create_slot(slot_index, prop_name, 'BLEND')
|
659 |
+
|
660 |
+
slot.use_rgb_to_intensity = True
|
661 |
+
mate.use_textures[slot_index] = False
|
662 |
+
|
663 |
+
fs = common.line_trim(lines[line_seek+2]).split(' ')
|
664 |
+
for fi in range(len(fs)):
|
665 |
+
fs[fi] = float(fs[fi])
|
666 |
+
slot.color = fs[:3]
|
667 |
+
slot.diffuse_color_factor = fs[3]
|
668 |
+
line_seek += 3
|
669 |
+
|
670 |
+
elif type == 'f':
|
671 |
+
prop_name = common.line_trim(lines[line_seek+1])
|
672 |
+
|
673 |
+
slot = search_or_create_slot(slot_index, prop_name, 'BLEND')
|
674 |
+
|
675 |
+
slot.use_rgb_to_intensity = False
|
676 |
+
mate.use_textures[slot_index] = False
|
677 |
+
slot.diffuse_color_factor = float(common.line_trim(lines[line_seek+2]))
|
678 |
+
line_seek += 3
|
679 |
+
|
680 |
+
else:
|
681 |
+
self.report(type={'ERROR'}, message="Unknown value found, mission aborted.")
|
682 |
+
return {'CANCELLED'}
|
683 |
+
slot_index += 1
|
684 |
+
|
685 |
+
# 存在しないスロットをクリア
|
686 |
+
for item_index in range(slot_index, len(mate.texture_slots)):
|
687 |
+
mate.texture_slots.clear(item_index)
|
688 |
+
|
689 |
+
# プレビューへの反映
|
690 |
+
for slot in mate.texture_slots:
|
691 |
+
if slot: common.set_texture_color(slot)
|
692 |
+
|
693 |
+
self.report(type={'INFO'}, message="Material was pasted.")
|
694 |
+
return {'FINISHED'}
|
695 |
+
|
696 |
+
class copy_material(bpy.types.Operator):
|
697 |
+
bl_idname = 'material.copy_material'
|
698 |
+
bl_label = "Copy Material to Clipboard"
|
699 |
+
bl_description = "Displayed material will be copied to the clipboard in text format."
|
700 |
+
bl_options = {'REGISTER', 'UNDO'}
|
701 |
+
|
702 |
+
@classmethod
|
703 |
+
def poll(cls, context):
|
704 |
+
if 'material' in dir(context):
|
705 |
+
mate = context.material
|
706 |
+
if mate:
|
707 |
+
return 'shader1' in mate and 'shader2' in mate
|
708 |
+
return False
|
709 |
+
|
710 |
+
def execute(self, context):
|
711 |
+
import re, os.path
|
712 |
+
|
713 |
+
mate = context.material
|
714 |
+
|
715 |
+
output_text = "1000" + "\n"
|
716 |
+
output_text += mate.name.lower() + "\n"
|
717 |
+
output_text += mate.name + "\n"
|
718 |
+
output_text += mate['shader1'] + "\n"
|
719 |
+
output_text += mate['shader2'] + "\n"
|
720 |
+
output_text += "\n"
|
721 |
+
|
722 |
+
for tex_slot in mate.texture_slots:
|
723 |
+
if not tex_slot:
|
724 |
+
continue
|
725 |
+
tex = tex_slot.texture
|
726 |
+
if tex_slot.use:
|
727 |
+
type = 'tex'
|
728 |
+
else:
|
729 |
+
if tex_slot.use_rgb_to_intensity:
|
730 |
+
type = 'col'
|
731 |
+
else:
|
732 |
+
type = 'f'
|
733 |
+
output_text += type + "\n"
|
734 |
+
output_text += "\t" + common.remove_serial_number(tex.name) + "\n"
|
735 |
+
if type == 'tex':
|
736 |
+
try:
|
737 |
+
img = tex.image
|
738 |
+
except:
|
739 |
+
self.report(type={'ERROR'}, message="Failed to acquire texture type. Mission aborted.")
|
740 |
+
return {'CANCELLED'}
|
741 |
+
if img:
|
742 |
+
output_text += '\ttex2d' + "\n"
|
743 |
+
output_text += "\t" + common.remove_serial_number(img.name) + "\n"
|
744 |
+
if 'cm3d2_path' in img:
|
745 |
+
path = img['cm3d2_path']
|
746 |
+
else:
|
747 |
+
path = bpy.path.abspath( bpy.path.abspath(img.filepath) )
|
748 |
+
path = path.replace('\\', '/')
|
749 |
+
path = re.sub(r'^[\/\.]*', "", path)
|
750 |
+
if not re.search(r'^assets/texture/', path, re.I):
|
751 |
+
path = "Assets/texture/texture/" + os.path.basename(path)
|
752 |
+
output_text += "\t" + path + "\n"
|
753 |
+
col = tex_slot.color
|
754 |
+
output_text += "\t" + " ".join([str(col[0]), str(col[1]), str(col[2]), str(tex_slot.diffuse_color_factor)]) + "\n"
|
755 |
+
else:
|
756 |
+
output_text += "\tnull" + "\n"
|
757 |
+
elif type == 'col':
|
758 |
+
col = tex_slot.color
|
759 |
+
output_text += "\t" + " ".join([str(col[0]), str(col[1]), str(col[2]), str(tex_slot.diffuse_color_factor)]) + "\n"
|
760 |
+
elif type == 'f':
|
761 |
+
output_text += "\t" + str(tex_slot.diffuse_color_factor) + "\n"
|
762 |
+
|
763 |
+
context.window_manager.clipboard = output_text
|
764 |
+
self.report(type={'INFO'}, message="Material copied to clipboard.")
|
765 |
+
return {'FINISHED'}
|
766 |
+
|
767 |
+
class decorate_material(bpy.types.Operator):
|
768 |
+
bl_idname = 'material.decorate_material'
|
769 |
+
bl_label = "Decorate Material"
|
770 |
+
bl_description = "Materials will be given the correct properties according to the settings."
|
771 |
+
bl_options = {'REGISTER', 'UNDO'}
|
772 |
+
|
773 |
+
@classmethod
|
774 |
+
def poll(cls, context):
|
775 |
+
ob = context.active_object
|
776 |
+
if not ob: return False
|
777 |
+
if ob.type != 'MESH': return False
|
778 |
+
for slot in ob.material_slots:
|
779 |
+
mate = slot.material
|
780 |
+
if mate:
|
781 |
+
if 'shader1' in mate and 'shader2' in mate:
|
782 |
+
return True
|
783 |
+
return False
|
784 |
+
|
785 |
+
def execute(self, context):
|
786 |
+
ob = context.active_object
|
787 |
+
me = ob.data
|
788 |
+
|
789 |
+
for slot_index, slot in enumerate(ob.material_slots):
|
790 |
+
mate = slot.material
|
791 |
+
if mate:
|
792 |
+
if 'shader1' in mate and 'shader2' in mate:
|
793 |
+
common.decorate_material(mate, True, me, slot_index)
|
794 |
+
|
795 |
+
return {'FINISHED'}
|
796 |
+
|
797 |
+
class quick_texture_show(bpy.types.Operator):
|
798 |
+
bl_idname = 'material.quick_texture_show'
|
799 |
+
bl_label = "See Texture"
|
800 |
+
bl_description = "See the texture."
|
801 |
+
bl_options = {'REGISTER'}
|
802 |
+
|
803 |
+
texture_name = bpy.props.StringProperty(name="Texture Name")
|
804 |
+
|
805 |
+
@classmethod
|
806 |
+
def poll(cls, context):
|
807 |
+
mate = context.material
|
808 |
+
if mate:
|
809 |
+
if 'shader1' in mate and 'shader2' in mate:
|
810 |
+
return True
|
811 |
+
return False
|
812 |
+
|
813 |
+
def execute(self, context):
|
814 |
+
mate = context.material
|
815 |
+
for index, slot in enumerate(mate.texture_slots):
|
816 |
+
if not slot: continue
|
817 |
+
if not slot.texture: continue
|
818 |
+
if slot.texture.name == self.texture_name:
|
819 |
+
mate.active_texture_index = index
|
820 |
+
context.space_data.context = 'TEXTURE'
|
821 |
+
break
|
822 |
+
return {'FINISHED'}
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_MESH_MT_shape_key_specials.py
ADDED
@@ -0,0 +1,575 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 「プロパティ」エリア → 「メッシュデータ」タブ → 「シェイプキー」パネル → ▼ボタン
|
2 |
+
import os, re, sys, bpy, time, bmesh, mathutils
|
3 |
+
from . import common
|
4 |
+
|
5 |
+
# メニュー等に項目追加
|
6 |
+
def menu_func(self, context):
|
7 |
+
icon_id = common.preview_collections['main']['KISS'].icon_id
|
8 |
+
self.layout.separator()
|
9 |
+
self.layout.operator('object.quick_shape_key_transfer', icon_value=icon_id)
|
10 |
+
self.layout.operator('object.precision_shape_key_transfer', icon_value=icon_id)
|
11 |
+
self.layout.separator()
|
12 |
+
self.layout.operator('object.multiply_shape_key', icon_value=icon_id)
|
13 |
+
self.layout.separator()
|
14 |
+
self.layout.operator('object.blur_shape_key', icon_value=icon_id)
|
15 |
+
self.layout.separator()
|
16 |
+
self.layout.operator('object.change_base_shape_key', icon_value=icon_id)
|
17 |
+
|
18 |
+
class quick_shape_key_transfer(bpy.types.Operator):
|
19 |
+
bl_idname = 'object.quick_shape_key_transfer'
|
20 |
+
bl_label = "Quick shape key transfer"
|
21 |
+
bl_description = "The shape keys from a previously selected object are transferred to the current object."
|
22 |
+
bl_options = {'REGISTER', 'UNDO'}
|
23 |
+
|
24 |
+
is_first_remove_all = bpy.props.BoolProperty(name="Remove All Previous Keys", default=True)
|
25 |
+
subdivide_number = bpy.props.IntProperty(name="Subdivide Amount", default=1, min=0, max=10, soft_min=0, soft_max=10)
|
26 |
+
is_remove_empty = bpy.props.BoolProperty(name="Remove Empty Keys", default=True)
|
27 |
+
|
28 |
+
@classmethod
|
29 |
+
def poll(cls, context):
|
30 |
+
active_ob = context.active_object
|
31 |
+
obs = context.selected_objects
|
32 |
+
if len(obs) != 2: return False
|
33 |
+
for ob in obs:
|
34 |
+
if ob.type != 'MESH':
|
35 |
+
return False
|
36 |
+
if ob.name != active_ob.name:
|
37 |
+
if ob.data.shape_keys:
|
38 |
+
return True
|
39 |
+
return False
|
40 |
+
|
41 |
+
def invoke(self, context, event):
|
42 |
+
return context.window_manager.invoke_props_dialog(self)
|
43 |
+
|
44 |
+
def draw(self, context):
|
45 |
+
self.layout.prop(self, 'is_first_remove_all', icon='ERROR')
|
46 |
+
self.layout.prop(self, 'subdivide_number', icon='LATTICE_DATA')
|
47 |
+
self.layout.prop(self, 'is_remove_empty', icon='X')
|
48 |
+
|
49 |
+
def execute(self, context):
|
50 |
+
import mathutils, time
|
51 |
+
start_time = time.time()
|
52 |
+
|
53 |
+
target_ob = context.active_object
|
54 |
+
target_me = target_ob.data
|
55 |
+
|
56 |
+
pre_mode = target_ob.mode
|
57 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
58 |
+
|
59 |
+
for ob in context.selected_objects:
|
60 |
+
if ob.name != target_ob.name:
|
61 |
+
source_original_ob = ob
|
62 |
+
break
|
63 |
+
source_ob = source_original_ob.copy()
|
64 |
+
source_me = source_original_ob.data.copy()
|
65 |
+
source_ob.data = source_me
|
66 |
+
context.scene.objects.link(source_ob)
|
67 |
+
context.scene.objects.active = source_ob
|
68 |
+
bpy.ops.object.mode_set(mode='EDIT')
|
69 |
+
bpy.ops.mesh.reveal()
|
70 |
+
bpy.ops.mesh.select_all(action='SELECT')
|
71 |
+
bpy.ops.mesh.subdivide(number_cuts=self.subdivide_number, smoothness=0.0, quadtri=False, quadcorner='STRAIGHT_CUT', fractal=0.0, fractal_along_normal=0.0, seed=0)
|
72 |
+
source_ob.active_shape_key_index = 0
|
73 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
74 |
+
|
75 |
+
if self.is_first_remove_all:
|
76 |
+
try:
|
77 |
+
target_ob.active_shape_key_index = 1
|
78 |
+
bpy.ops.object.shape_key_remove(all=True)
|
79 |
+
except:
|
80 |
+
pass
|
81 |
+
|
82 |
+
kd = mathutils.kdtree.KDTree(len(source_me.vertices))
|
83 |
+
for vert in source_me.vertices:
|
84 |
+
co = source_ob.matrix_world * vert.co
|
85 |
+
kd.insert(co, vert.index)
|
86 |
+
kd.balance()
|
87 |
+
|
88 |
+
near_vert_indexs = [kd.find(target_ob.matrix_world * v.co)[1] for v in target_me.vertices]
|
89 |
+
|
90 |
+
is_shapeds = {}
|
91 |
+
relative_keys = []
|
92 |
+
context.window_manager.progress_begin(0, len(source_me.shape_keys.key_blocks))
|
93 |
+
context.window_manager.progress_update(0)
|
94 |
+
for source_shape_key_index, source_shape_key in enumerate(source_me.shape_keys.key_blocks):
|
95 |
+
|
96 |
+
if target_me.shape_keys:
|
97 |
+
if source_shape_key.name in target_me.shape_keys.key_blocks:
|
98 |
+
target_shape_key = target_me.shape_keys.key_blocks[source_shape_key.name]
|
99 |
+
else:
|
100 |
+
target_shape_key = target_ob.shape_key_add(name=source_shape_key.name, from_mix=False)
|
101 |
+
else:
|
102 |
+
target_shape_key = target_ob.shape_key_add(name=source_shape_key.name, from_mix=False)
|
103 |
+
|
104 |
+
relative_key_name = source_shape_key.relative_key.name
|
105 |
+
if relative_key_name not in relative_keys:
|
106 |
+
relative_keys.append(relative_key_name)
|
107 |
+
is_shapeds[source_shape_key.name] = False
|
108 |
+
|
109 |
+
try:
|
110 |
+
target_shape_key.relative_key = target_me.shape_keys.key_blocks[relative_key_name]
|
111 |
+
except:
|
112 |
+
pass
|
113 |
+
|
114 |
+
mat1, mat2 = source_ob.matrix_world, target_ob.matrix_world
|
115 |
+
source_shape_keys = [(mat1 * source_shape_key.data[v.index].co * mat2) - (mat1 * source_me.vertices[v.index].co * mat2) for v in source_me.vertices]
|
116 |
+
|
117 |
+
for target_vert in target_me.vertices:
|
118 |
+
|
119 |
+
near_vert_index = near_vert_indexs[target_vert.index]
|
120 |
+
near_shape_co = source_shape_keys[near_vert_index]
|
121 |
+
|
122 |
+
target_shape_key.data[target_vert.index].co = target_me.vertices[target_vert.index].co + near_shape_co
|
123 |
+
if 0.01 < near_shape_co.length:
|
124 |
+
is_shapeds[source_shape_key.name] = True
|
125 |
+
|
126 |
+
context.window_manager.progress_update(source_shape_key_index)
|
127 |
+
context.window_manager.progress_end()
|
128 |
+
|
129 |
+
if self.is_remove_empty:
|
130 |
+
for source_shape_key_name, is_shaped in is_shapeds.items():
|
131 |
+
if source_shape_key_name not in relative_keys and not is_shaped:
|
132 |
+
target_shape_key = target_me.shape_keys.key_blocks[source_shape_key_name]
|
133 |
+
target_ob.shape_key_remove(target_shape_key)
|
134 |
+
|
135 |
+
target_ob.active_shape_key_index = 0
|
136 |
+
|
137 |
+
common.remove_data([source_ob, source_me])
|
138 |
+
context.scene.objects.active = target_ob
|
139 |
+
bpy.ops.object.mode_set(mode=pre_mode)
|
140 |
+
|
141 |
+
diff_time = time.time() - start_time
|
142 |
+
self.report(type={'INFO'}, message=str(round(diff_time, 1)) + " Seconds")
|
143 |
+
return {'FINISHED'}
|
144 |
+
|
145 |
+
class precision_shape_key_transfer(bpy.types.Operator):
|
146 |
+
bl_idname = 'object.precision_shape_key_transfer'
|
147 |
+
bl_label = "Precision Shape key Transfer"
|
148 |
+
bl_description = "High precision transfer of shape keys from a previously selected object to the active object."
|
149 |
+
bl_options = {'REGISTER', 'UNDO'}
|
150 |
+
|
151 |
+
is_first_remove_all = bpy.props.BoolProperty(name="Remove All Previous Keys", default=True)
|
152 |
+
subdivide_number = bpy.props.IntProperty(name="Subdivide Amount", default=1, min=0, max=10, soft_min=0, soft_max=10)
|
153 |
+
extend_range = bpy.props.FloatProperty(name="Range", default=1.5, min=1.1, max=5.0, soft_min=1.1, soft_max=5.0, step=10, precision=2)
|
154 |
+
is_remove_empty = bpy.props.BoolProperty(name="Remove Empty Keys", default=True)
|
155 |
+
|
156 |
+
@classmethod
|
157 |
+
def poll(cls, context):
|
158 |
+
active_ob = context.active_object
|
159 |
+
obs = context.selected_objects
|
160 |
+
if len(obs) != 2: return False
|
161 |
+
for ob in obs:
|
162 |
+
if ob.type != 'MESH':
|
163 |
+
return False
|
164 |
+
if ob.name != active_ob.name:
|
165 |
+
if ob.data.shape_keys:
|
166 |
+
return True
|
167 |
+
return False
|
168 |
+
|
169 |
+
def invoke(self, context, event):
|
170 |
+
return context.window_manager.invoke_props_dialog(self)
|
171 |
+
|
172 |
+
def draw(self, context):
|
173 |
+
self.layout.prop(self, 'is_first_remove_all', icon='ERROR')
|
174 |
+
self.layout.prop(self, 'subdivide_number', icon='LATTICE_DATA')
|
175 |
+
self.layout.prop(self, 'extend_range', icon='META_EMPTY')
|
176 |
+
self.layout.prop(self, 'is_remove_empty', icon='X')
|
177 |
+
|
178 |
+
def execute(self, context):
|
179 |
+
import mathutils, time
|
180 |
+
start_time = time.time()
|
181 |
+
|
182 |
+
target_ob = context.active_object
|
183 |
+
target_me = target_ob.data
|
184 |
+
|
185 |
+
pre_mode = target_ob.mode
|
186 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
187 |
+
|
188 |
+
for ob in context.selected_objects:
|
189 |
+
if ob.name != target_ob.name:
|
190 |
+
source_original_ob = ob
|
191 |
+
break
|
192 |
+
source_ob = source_original_ob.copy()
|
193 |
+
source_me = source_original_ob.data.copy()
|
194 |
+
source_ob.data = source_me
|
195 |
+
context.scene.objects.link(source_ob)
|
196 |
+
context.scene.objects.active = source_ob
|
197 |
+
bpy.ops.object.mode_set(mode='EDIT')
|
198 |
+
bpy.ops.mesh.reveal()
|
199 |
+
bpy.ops.mesh.select_all(action='SELECT')
|
200 |
+
bpy.ops.mesh.subdivide(number_cuts=self.subdivide_number, smoothness=0.0, quadtri=False, quadcorner='STRAIGHT_CUT', fractal=0.0, fractal_along_normal=0.0, seed=0)
|
201 |
+
source_ob.active_shape_key_index = 0
|
202 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
203 |
+
|
204 |
+
if self.is_first_remove_all:
|
205 |
+
try:
|
206 |
+
target_ob.active_shape_key_index = 1
|
207 |
+
bpy.ops.object.shape_key_remove(all=True)
|
208 |
+
except:
|
209 |
+
pass
|
210 |
+
|
211 |
+
kd = mathutils.kdtree.KDTree(len(source_me.vertices))
|
212 |
+
for vert in source_me.vertices:
|
213 |
+
co = source_ob.matrix_world * vert.co
|
214 |
+
kd.insert(co, vert.index)
|
215 |
+
kd.balance()
|
216 |
+
|
217 |
+
context.window_manager.progress_begin(0, len(target_me.vertices))
|
218 |
+
progress_reduce = len(target_me.vertices) // 200 + 1
|
219 |
+
near_vert_data = []
|
220 |
+
near_vert_multi_total = []
|
221 |
+
near_vert_multi_total_append = near_vert_multi_total.append
|
222 |
+
for vert in target_me.vertices:
|
223 |
+
near_vert_data.append([])
|
224 |
+
near_vert_data_append = near_vert_data[-1].append
|
225 |
+
|
226 |
+
target_co = target_ob.matrix_world * vert.co
|
227 |
+
mini_co, mini_index, mini_dist = kd.find(target_co)
|
228 |
+
radius = mini_dist * self.extend_range
|
229 |
+
diff_radius = radius - mini_dist
|
230 |
+
|
231 |
+
multi_total = 0.0
|
232 |
+
for co, index, dist in kd.find_range(target_co, radius):
|
233 |
+
if 0 < diff_radius:
|
234 |
+
multi = (diff_radius - (dist - mini_dist)) / diff_radius
|
235 |
+
else:
|
236 |
+
multi = 1.0
|
237 |
+
near_vert_data_append((index, multi))
|
238 |
+
multi_total += multi
|
239 |
+
near_vert_multi_total_append(multi_total)
|
240 |
+
|
241 |
+
if vert.index % progress_reduce == 0:
|
242 |
+
context.window_manager.progress_update(vert.index)
|
243 |
+
context.window_manager.progress_end()
|
244 |
+
|
245 |
+
is_shapeds = {}
|
246 |
+
relative_keys = []
|
247 |
+
context.window_manager.progress_begin(0, len(source_me.shape_keys.key_blocks))
|
248 |
+
context.window_manager.progress_update(0)
|
249 |
+
for source_shape_key_index, source_shape_key in enumerate(source_me.shape_keys.key_blocks):
|
250 |
+
|
251 |
+
if target_me.shape_keys:
|
252 |
+
if source_shape_key.name in target_me.shape_keys.key_blocks:
|
253 |
+
target_shape_key = target_me.shape_keys.key_blocks[source_shape_key.name]
|
254 |
+
else:
|
255 |
+
target_shape_key = target_ob.shape_key_add(name=source_shape_key.name, from_mix=False)
|
256 |
+
else:
|
257 |
+
target_shape_key = target_ob.shape_key_add(name=source_shape_key.name, from_mix=False)
|
258 |
+
|
259 |
+
relative_key_name = source_shape_key.relative_key.name
|
260 |
+
if relative_key_name not in relative_keys:
|
261 |
+
relative_keys.append(relative_key_name)
|
262 |
+
is_shapeds[source_shape_key.name] = False
|
263 |
+
|
264 |
+
try:
|
265 |
+
target_shape_key.relative_key = target_me.shape_keys.key_blocks[relative_key_name]
|
266 |
+
except:
|
267 |
+
pass
|
268 |
+
|
269 |
+
mat1, mat2 = source_ob.matrix_world, target_ob.matrix_world
|
270 |
+
source_shape_keys = [(mat1 * source_shape_key.data[v.index].co * mat2) - (mat1 * source_me.vertices[v.index].co * mat2) for v in source_me.vertices]
|
271 |
+
|
272 |
+
for target_vert in target_me.vertices:
|
273 |
+
|
274 |
+
if 0 < near_vert_multi_total[target_vert.index]:
|
275 |
+
|
276 |
+
total_diff_co = mathutils.Vector((0, 0, 0))
|
277 |
+
|
278 |
+
for near_index, near_multi in near_vert_data[target_vert.index]:
|
279 |
+
total_diff_co += source_shape_keys[near_index] * near_multi
|
280 |
+
|
281 |
+
average_diff_co = total_diff_co / near_vert_multi_total[target_vert.index]
|
282 |
+
|
283 |
+
else:
|
284 |
+
average_diff_co = mathutils.Vector((0, 0, 0))
|
285 |
+
|
286 |
+
target_shape_key.data[target_vert.index].co = target_me.vertices[target_vert.index].co + average_diff_co
|
287 |
+
if 0.01 < average_diff_co.length:
|
288 |
+
is_shapeds[source_shape_key.name] = True
|
289 |
+
|
290 |
+
context.window_manager.progress_update(source_shape_key_index)
|
291 |
+
context.window_manager.progress_end()
|
292 |
+
|
293 |
+
if self.is_remove_empty:
|
294 |
+
for source_shape_key_name, is_shaped in is_shapeds.items():
|
295 |
+
if source_shape_key_name not in relative_keys and not is_shaped:
|
296 |
+
target_shape_key = target_me.shape_keys.key_blocks[source_shape_key_name]
|
297 |
+
target_ob.shape_key_remove(target_shape_key)
|
298 |
+
|
299 |
+
target_ob.active_shape_key_index = 0
|
300 |
+
|
301 |
+
common.remove_data([source_ob, source_me])
|
302 |
+
context.scene.objects.active = target_ob
|
303 |
+
bpy.ops.object.mode_set(mode=pre_mode)
|
304 |
+
|
305 |
+
diff_time = time.time() - start_time
|
306 |
+
self.report(type={'INFO'}, message=str(round(diff_time, 1)) + " Seconds")
|
307 |
+
return {'FINISHED'}
|
308 |
+
|
309 |
+
class multiply_shape_key(bpy.types.Operator):
|
310 |
+
bl_idname = 'object.multiply_shape_key'
|
311 |
+
bl_label = "Multiply Shape Key"
|
312 |
+
bl_description = "Multiply the current shape key so that other shape keys are more affected by it"
|
313 |
+
bl_options = {'REGISTER', 'UNDO'}
|
314 |
+
|
315 |
+
multi = bpy.props.FloatProperty(name="Scale", description="Shape Key Scale Ratio", default=1.1, min=-10, max=10, soft_min=-10, soft_max=10, step=10, precision=2)
|
316 |
+
items = [
|
317 |
+
('ACTIVE', "Active only", "", 'HAND', 1),
|
318 |
+
('UP', "Above Active", "", 'TRIA_UP_BAR', 2),
|
319 |
+
('DOWN', "Below Active", "", 'TRIA_DOWN_BAR', 3),
|
320 |
+
('ALL', "All", "", 'ARROW_LEFTRIGHT', 4),
|
321 |
+
]
|
322 |
+
mode = bpy.props.EnumProperty(items=items, name="対象", default='ACTIVE')
|
323 |
+
|
324 |
+
@classmethod
|
325 |
+
def poll(cls, context):
|
326 |
+
if context.active_object:
|
327 |
+
ob = context.active_object
|
328 |
+
if ob.type == 'MESH':
|
329 |
+
return ob.active_shape_key
|
330 |
+
return False
|
331 |
+
|
332 |
+
def invoke(self, context, event):
|
333 |
+
return context.window_manager.invoke_props_dialog(self)
|
334 |
+
|
335 |
+
def draw(self, context):
|
336 |
+
self.layout.prop(self, 'multi', icon='ARROW_LEFTRIGHT')
|
337 |
+
self.layout.prop(self, 'mode', icon='VIEWZOOM')
|
338 |
+
|
339 |
+
def execute(self, context):
|
340 |
+
ob = context.active_object
|
341 |
+
me = ob.data
|
342 |
+
shape_keys = me.shape_keys
|
343 |
+
pre_mode = ob.mode
|
344 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
345 |
+
|
346 |
+
target_shapes = []
|
347 |
+
if self.mode == 'ACTIVE':
|
348 |
+
target_shapes.append(ob.active_shape_key)
|
349 |
+
elif self.mode == 'UP':
|
350 |
+
for index, key_block in enumerate(shape_keys.key_blocks):
|
351 |
+
if index <= ob.active_shape_key_index:
|
352 |
+
target_shapes.append(key_block)
|
353 |
+
elif self.mode == 'UP':
|
354 |
+
for index, key_block in enumerate(shape_keys.key_blocks):
|
355 |
+
if ob.active_shape_key_index <= index:
|
356 |
+
target_shapes.append(key_block)
|
357 |
+
elif self.mode == 'ALL':
|
358 |
+
for key_block in shape_keys.key_blocks:
|
359 |
+
target_shapes.append(key_block)
|
360 |
+
|
361 |
+
for shape in target_shapes:
|
362 |
+
data = shape.data
|
363 |
+
for i, vert in enumerate(me.vertices):
|
364 |
+
diff = data[i].co - vert.co
|
365 |
+
diff *= self.multi
|
366 |
+
data[i].co = vert.co + diff
|
367 |
+
bpy.ops.object.mode_set(mode=pre_mode)
|
368 |
+
return {'FINISHED'}
|
369 |
+
|
370 |
+
class blur_shape_key(bpy.types.Operator):
|
371 |
+
bl_idname = 'object.blur_shape_key'
|
372 |
+
bl_label = "Blur Shape Key"
|
373 |
+
bl_description = "Blur all shape keys or just the active key."
|
374 |
+
bl_options = {'REGISTER', 'UNDO'}
|
375 |
+
|
376 |
+
items = [
|
377 |
+
('ACTIVE', "Active Only", "", 'HAND', 1),
|
378 |
+
('UP', "Above Active", "", 'TRIA_UP_BAR', 2),
|
379 |
+
('DOWN', "Below Active", "", 'TRIA_DOWN_BAR', 3),
|
380 |
+
('ALL', "All", "", 'ARROW_LEFTRIGHT', 4),
|
381 |
+
]
|
382 |
+
target = bpy.props.EnumProperty(items=items, name="Active", default='ACTIVE')
|
383 |
+
radius = bpy.props.FloatProperty(name="Radius", default=3, min=0.1, max=50, soft_min=0.1, soft_max=50, step=50, precision=2)
|
384 |
+
strength = bpy.props.IntProperty(name="Strength", default=1, min=1, max=10, soft_min=1, soft_max=10)
|
385 |
+
items = [
|
386 |
+
('BOTH', "Both", "", 'AUTOMERGE_ON', 1),
|
387 |
+
('ADD', "Add", "", 'TRIA_UP', 2),
|
388 |
+
('SUB', "Subtract", "", 'TRIA_DOWN', 3),
|
389 |
+
]
|
390 |
+
effect = bpy.props.EnumProperty(items=items, name="Blur Effect", default='BOTH')
|
391 |
+
items = [
|
392 |
+
('LINER', "Liner", "", 'LINCURVE', 1),
|
393 |
+
('SMOOTH1', "Smooth 1", "", 'SMOOTHCURVE', 2),
|
394 |
+
('SMOOTH2', "Smooth 2", "", 'SMOOTHCURVE', 3),
|
395 |
+
]
|
396 |
+
blend = bpy.props.EnumProperty(items=items, name="Damping Type", default='LINER')
|
397 |
+
|
398 |
+
@classmethod
|
399 |
+
def poll(cls, context):
|
400 |
+
ob = context.active_object
|
401 |
+
if ob:
|
402 |
+
if ob.type == 'MESH':
|
403 |
+
me = ob.data
|
404 |
+
return me.shape_keys
|
405 |
+
return False
|
406 |
+
|
407 |
+
def invoke(self, context, event):
|
408 |
+
return context.window_manager.invoke_props_dialog(self)
|
409 |
+
|
410 |
+
def draw(self, context):
|
411 |
+
self.layout.prop(self, 'target', icon='VIEWZOOM')
|
412 |
+
self.layout.prop(self, 'radius', icon='META_EMPTY')
|
413 |
+
self.layout.prop(self, 'strength', icon='ARROW_LEFTRIGHT')
|
414 |
+
self.layout.prop(self, 'effect', icon='BRUSH_BLUR')
|
415 |
+
self.layout.prop(self, 'blend', icon='IPO_SINE')
|
416 |
+
|
417 |
+
def execute(self, context):
|
418 |
+
import bmesh, mathutils
|
419 |
+
ob = context.active_object
|
420 |
+
me = ob.data
|
421 |
+
|
422 |
+
pre_mode = ob.mode
|
423 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
424 |
+
|
425 |
+
bm = bmesh.new()
|
426 |
+
bm.from_mesh(me)
|
427 |
+
edge_lengths = [e.calc_length() for e in bm.edges]
|
428 |
+
bm.free()
|
429 |
+
edge_lengths.sort()
|
430 |
+
average_edge_length = sum(edge_lengths) / len(edge_lengths)
|
431 |
+
center_index = int( (len(edge_lengths) - 1) / 2.0 )
|
432 |
+
average_edge_length = (average_edge_length + edge_lengths[center_index]) / 2
|
433 |
+
radius = average_edge_length * self.radius
|
434 |
+
|
435 |
+
context.window_manager.progress_begin(0, len(me.vertices))
|
436 |
+
progress_reduce = len(me.vertices) // 200 + 1
|
437 |
+
near_vert_data = []
|
438 |
+
kd = mathutils.kdtree.KDTree(len(me.vertices))
|
439 |
+
for vert in me.vertices:
|
440 |
+
kd.insert(vert.co.copy(), vert.index)
|
441 |
+
kd.balance()
|
442 |
+
for vert in me.vertices:
|
443 |
+
near_vert_data.append([])
|
444 |
+
near_vert_data_append = near_vert_data[-1].append
|
445 |
+
for co, index, dist in kd.find_range(vert.co, radius):
|
446 |
+
multi = (radius - dist) / radius
|
447 |
+
if self.blend == 'SMOOTH1':
|
448 |
+
multi = common.in_out_quad_blend(multi)
|
449 |
+
elif self.blend == 'SMOOTH2':
|
450 |
+
multi = common.bezier_blend(multi)
|
451 |
+
near_vert_data_append((index, multi))
|
452 |
+
if vert.index % progress_reduce == 0:
|
453 |
+
context.window_manager.progress_update(vert.index)
|
454 |
+
context.window_manager.progress_end()
|
455 |
+
|
456 |
+
target_shape_keys = []
|
457 |
+
if self.target == 'ACTIVE':
|
458 |
+
target_shape_keys.append(ob.active_shape_key)
|
459 |
+
elif self.target == 'UP':
|
460 |
+
for index, shape_key in enumerate(me.shape_keys.key_blocks):
|
461 |
+
if index <= ob.active_shape_key_index:
|
462 |
+
target_shape_keys.append(shape_key)
|
463 |
+
elif self.target == 'DOWN':
|
464 |
+
for index, shape_key in enumerate(me.shape_keys.key_blocks):
|
465 |
+
if ob.active_shape_key_index <= index:
|
466 |
+
target_shape_keys.append(shape_key)
|
467 |
+
elif self.target == 'ALL':
|
468 |
+
for index, shape_key in enumerate(me.shape_keys.key_blocks):
|
469 |
+
target_shape_keys.append(shape_key)
|
470 |
+
|
471 |
+
progress_total = len(target_shape_keys) * self.strength * len(me.vertices)
|
472 |
+
context.window_manager.progress_begin(0, progress_total)
|
473 |
+
progress_reduce = progress_total // 200 + 1
|
474 |
+
progress_count = 0
|
475 |
+
for strength_count in range(self.strength):
|
476 |
+
for shape_key in target_shape_keys:
|
477 |
+
|
478 |
+
shapes = []
|
479 |
+
shapes_append = shapes.append
|
480 |
+
for index, vert in enumerate(me.vertices):
|
481 |
+
co = shape_key.data[index].co - vert.co
|
482 |
+
shapes_append(co)
|
483 |
+
|
484 |
+
for vert in me.vertices:
|
485 |
+
|
486 |
+
target_shape = shapes[vert.index]
|
487 |
+
|
488 |
+
total_shape = mathutils.Vector()
|
489 |
+
total_multi = 0.0
|
490 |
+
for index, multi in near_vert_data[vert.index]:
|
491 |
+
co = shapes[index]
|
492 |
+
if self.effect == 'ADD':
|
493 |
+
if target_shape.length <= co.length:
|
494 |
+
total_shape += co * multi
|
495 |
+
total_multi += multi
|
496 |
+
elif self.effect == 'SUB':
|
497 |
+
if co.length <= target_shape.length:
|
498 |
+
total_shape += co * multi
|
499 |
+
total_multi += multi
|
500 |
+
else:
|
501 |
+
total_shape += co * multi
|
502 |
+
total_multi += multi
|
503 |
+
|
504 |
+
if 0 < total_multi:
|
505 |
+
average_shape = total_shape / total_multi
|
506 |
+
else:
|
507 |
+
average_shape = mathutils.Vector()
|
508 |
+
|
509 |
+
shape_key.data[vert.index].co = vert.co + average_shape
|
510 |
+
|
511 |
+
progress_count += 1
|
512 |
+
if progress_count % progress_reduce == 0:
|
513 |
+
context.window_manager.progress_update(progress_count)
|
514 |
+
|
515 |
+
context.window_manager.progress_end()
|
516 |
+
bpy.ops.object.mode_set(mode=pre_mode)
|
517 |
+
return {'FINISHED'}
|
518 |
+
|
519 |
+
class change_base_shape_key(bpy.types.Operator):
|
520 |
+
bl_idname = 'object.change_base_shape_key'
|
521 |
+
bl_label = "Change Base Key"
|
522 |
+
bl_description = "Will change the key on which the mesh is based."
|
523 |
+
bl_options = {'REGISTER', 'UNDO'}
|
524 |
+
|
525 |
+
is_deform_mesh = bpy.props.BoolProperty(name="Deform Mesh", default=True)
|
526 |
+
is_deform_other_shape = bpy.props.BoolProperty(name="Deform other Keys", default=True)
|
527 |
+
|
528 |
+
@classmethod
|
529 |
+
def poll(cls, context):
|
530 |
+
ob = context.active_object
|
531 |
+
if ob:
|
532 |
+
return ob.type=='MESH' and 1 <= ob.active_shape_key_index
|
533 |
+
return False
|
534 |
+
|
535 |
+
def invoke(self, context, event):
|
536 |
+
return context.window_manager.invoke_props_dialog(self)
|
537 |
+
|
538 |
+
def draw(self, context):
|
539 |
+
self.layout.prop(self, 'is_deform_mesh', icon='MESH_DATA')
|
540 |
+
self.layout.prop(self, 'is_deform_other_shape', icon='SHAPEKEY_DATA')
|
541 |
+
|
542 |
+
def execute(self, context):
|
543 |
+
ob = context.active_object
|
544 |
+
me = ob.data
|
545 |
+
|
546 |
+
pre_mode = ob.mode
|
547 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
548 |
+
|
549 |
+
target_shape_key = ob.active_shape_key
|
550 |
+
old_shape_key = me.shape_keys.key_blocks[0]
|
551 |
+
|
552 |
+
for i in range(9**9):
|
553 |
+
bpy.ops.object.shape_key_move(type='UP')
|
554 |
+
if ob.active_shape_key_index == 0:
|
555 |
+
break
|
556 |
+
|
557 |
+
target_shape_key.relative_key = target_shape_key
|
558 |
+
old_shape_key.relative_key = target_shape_key
|
559 |
+
|
560 |
+
if self.is_deform_mesh:
|
561 |
+
for vert in me.vertices:
|
562 |
+
vert.co = target_shape_key.data[vert.index].co.copy()
|
563 |
+
|
564 |
+
if self.is_deform_other_shape:
|
565 |
+
for shape_key in me.shape_keys.key_blocks:
|
566 |
+
if shape_key.name == target_shape_key.name or shape_key.name == old_shape_key.name:
|
567 |
+
continue
|
568 |
+
if shape_key.relative_key.name == old_shape_key.name:
|
569 |
+
shape_key.relative_key = target_shape_key
|
570 |
+
for vert in me.vertices:
|
571 |
+
diff_co = target_shape_key.data[vert.index].co - old_shape_key.data[vert.index].co
|
572 |
+
shape_key.data[vert.index].co = shape_key.data[vert.index].co + diff_co
|
573 |
+
|
574 |
+
bpy.ops.object.mode_set(mode=pre_mode)
|
575 |
+
return {'FINISHED'}
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_MESH_MT_vertex_group_specials.py
ADDED
@@ -0,0 +1,672 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 「プロパティ」エリア → 「メッシュデータ」タブ → 「頂点グループ」パネル → ▼ボタン
|
2 |
+
import os, re, sys, bpy, time, bmesh, mathutils
|
3 |
+
from . import common
|
4 |
+
|
5 |
+
# メニュー等に項目追加
|
6 |
+
def menu_func(self, context):
|
7 |
+
icon_id = common.preview_collections['main']['KISS'].icon_id
|
8 |
+
self.layout.separator()
|
9 |
+
self.layout.operator('object.quick_transfer_vertex_group', icon_value=icon_id)
|
10 |
+
self.layout.operator('object.precision_transfer_vertex_group', icon_value=icon_id)
|
11 |
+
self.layout.separator()
|
12 |
+
self.layout.operator('object.quick_blur_vertex_group', icon_value=icon_id)
|
13 |
+
self.layout.operator('object.blur_vertex_group', icon_value=icon_id)
|
14 |
+
self.layout.separator()
|
15 |
+
self.layout.operator('object.multiply_vertex_group', icon_value=icon_id)
|
16 |
+
self.layout.separator()
|
17 |
+
self.layout.operator('object.remove_noassign_vertex_groups', icon_value=icon_id)
|
18 |
+
|
19 |
+
class quick_transfer_vertex_group(bpy.types.Operator):
|
20 |
+
bl_idname = 'object.quick_transfer_vertex_group'
|
21 |
+
bl_label = "Quick Vertex Group Transfer"
|
22 |
+
bl_description = "Quickly Transfers the vertex groups of the previously selected mesh to active mesh."
|
23 |
+
bl_options = {'REGISTER', 'UNDO'}
|
24 |
+
|
25 |
+
is_remove_old_vertex_groups = bpy.props.BoolProperty(name="すでにある頂点グループを削除 (ロックで保護)", default=False)
|
26 |
+
is_source_select_vert_only = bpy.props.BoolProperty(name="選択頂点のみ(参照)", default=False)
|
27 |
+
is_target_select_vert_only = bpy.props.BoolProperty(name="選択頂点のみ(対象)", default=False)
|
28 |
+
items = [
|
29 |
+
('NEAREST', "Nearest", "", 'VERTEXSEL', 1),
|
30 |
+
('EDGEINTERP_NEAREST', "Nearest Side", "", 'EDGESEL', 2),
|
31 |
+
('POLYINTERP_NEAREST', "Nearest Face", "", 'FACESEL', 3),
|
32 |
+
('POLYINTERP_VNORPROJ', "Projection", "", 'MOD_UVPROJECT', 4),
|
33 |
+
]
|
34 |
+
vert_mapping = bpy.props.EnumProperty(items=items, name="Reference element", default='POLYINTERP_NEAREST')
|
35 |
+
is_clean = bpy.props.BoolProperty(name="Clean after Transfer", default=True)
|
36 |
+
is_remove_noassign = bpy.props.BoolProperty(name="Delete unassigned vertex groups after transfer", default=True)
|
37 |
+
|
38 |
+
@classmethod
|
39 |
+
def poll(cls, context):
|
40 |
+
active_ob = context.active_object
|
41 |
+
obs = context.selected_objects
|
42 |
+
if len(obs) < 2: return False
|
43 |
+
for ob in obs:
|
44 |
+
if ob.type != 'MESH':
|
45 |
+
return False
|
46 |
+
if ob.name != active_ob.name:
|
47 |
+
if not len(ob.vertex_groups):
|
48 |
+
return False
|
49 |
+
return True
|
50 |
+
|
51 |
+
def invoke(self, context, event):
|
52 |
+
return context.window_manager.invoke_props_dialog(self)
|
53 |
+
|
54 |
+
def draw(self, context):
|
55 |
+
self.layout.prop(self, 'is_remove_old_vertex_groups', icon='ERROR')
|
56 |
+
|
57 |
+
row = self.layout.row(align=True)
|
58 |
+
row.prop(self, 'is_source_select_vert_only', icon='UV_SYNC_SELECT')
|
59 |
+
row.prop(self, 'is_target_select_vert_only', icon='UV_SYNC_SELECT')
|
60 |
+
|
61 |
+
self.layout.prop(self, 'vert_mapping')
|
62 |
+
self.layout.prop(self, 'is_clean', icon='DISCLOSURE_TRI_DOWN')
|
63 |
+
self.layout.prop(self, 'is_remove_noassign', icon='X')
|
64 |
+
|
65 |
+
def execute(self, context):
|
66 |
+
class MyVertexGroupElement: pass
|
67 |
+
|
68 |
+
target_ob = context.active_object
|
69 |
+
target_me = target_ob.data
|
70 |
+
|
71 |
+
pre_mode = target_ob.mode
|
72 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
73 |
+
|
74 |
+
original_source_obs = []
|
75 |
+
for ob in context.selected_objects:
|
76 |
+
if ob.name != target_ob.name:
|
77 |
+
original_source_obs.append(ob)
|
78 |
+
|
79 |
+
target_ob.select = False
|
80 |
+
context.scene.objects.active = original_source_obs[0]
|
81 |
+
bpy.ops.object.duplicate(linked=False, mode='TRANSLATION')
|
82 |
+
bpy.ops.object.join()
|
83 |
+
join_source_ob = context.selected_objects[0]
|
84 |
+
join_source_me = join_source_ob.data
|
85 |
+
target_ob.select = True
|
86 |
+
context.scene.objects.active = target_ob
|
87 |
+
|
88 |
+
temp_source_ob = join_source_ob.copy()
|
89 |
+
temp_source_me = join_source_me.copy()
|
90 |
+
temp_source_ob.data = temp_source_me
|
91 |
+
context.scene.objects.link(temp_source_ob)
|
92 |
+
temp_source_ob.select = True
|
93 |
+
join_source_ob.select = False
|
94 |
+
context.scene.objects.active = temp_source_ob
|
95 |
+
if self.is_source_select_vert_only:
|
96 |
+
bpy.ops.object.mode_set(mode='EDIT')
|
97 |
+
bpy.ops.mesh.select_all(action='INVERT')
|
98 |
+
bpy.ops.mesh.delete(type='VERT')
|
99 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
100 |
+
context.scene.objects.active = target_ob
|
101 |
+
|
102 |
+
if self.vert_mapping == 'POLYINTERP_VNORPROJ' and len(temp_source_me.polygons) == 0:
|
103 |
+
self.vert_mapping = 'EDGEINTERP_NEAREST'
|
104 |
+
self.report(type={'WARNING'}, message="There is no face, change to edge mode")
|
105 |
+
if self.vert_mapping == 'POLYINTERP_NEAREST' and len(temp_source_me.polygons) == 0:
|
106 |
+
self.vert_mapping = 'EDGEINTERP_NEAREST'
|
107 |
+
self.report(type={'WARNING'}, message="There is no face, change to edge mode")
|
108 |
+
if self.vert_mapping == 'EDGEINTERP_NEAREST' and len(temp_source_me.edges) == 0:
|
109 |
+
self.vert_mapping = 'NEAREST'
|
110 |
+
self.report(type={'WARNING'}, message="There is no edge, change to vertex mode")
|
111 |
+
if self.vert_mapping == 'NEAREST' and len(temp_source_me.vertices) == 0:
|
112 |
+
self.report(type={'ERROR'}, message="There is no edge, change to vertex mode")
|
113 |
+
return {'CANCELLED'}
|
114 |
+
|
115 |
+
if self.is_remove_old_vertex_groups:
|
116 |
+
for vg in target_ob.vertex_groups[:]:
|
117 |
+
if not vg.lock_weight:
|
118 |
+
target_ob.vertex_groups.remove(vg)
|
119 |
+
|
120 |
+
old_vertex_groups = []
|
121 |
+
for vert in target_me.vertices:
|
122 |
+
mvges = []
|
123 |
+
for vge in vert.groups:
|
124 |
+
mvge = MyVertexGroupElement()
|
125 |
+
mvge.vertex_group = target_ob.vertex_groups[vge.group]
|
126 |
+
mvge.weight = vge.weight
|
127 |
+
mvges.append(mvge)
|
128 |
+
old_vertex_groups.append(mvges)
|
129 |
+
|
130 |
+
if self.is_remove_noassign:
|
131 |
+
pre_vertex_group_names = [vg.name for vg in target_ob.vertex_groups]
|
132 |
+
|
133 |
+
bpy.ops.object.data_transfer(use_reverse_transfer=True, use_freeze=False, data_type='VGROUP_WEIGHTS', use_create=True, vert_mapping=self.vert_mapping, use_auto_transform=False, use_object_transform=True, use_max_distance=False, ray_radius=0, layers_select_src='NAME', layers_select_dst='ALL', mix_mode='REPLACE', mix_factor=1)
|
134 |
+
if self.is_clean: bpy.ops.object.vertex_group_clean(group_select_mode='ALL', limit=0.00000000001)
|
135 |
+
|
136 |
+
bpy.ops.object.mode_set(mode='EDIT')
|
137 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
138 |
+
|
139 |
+
if self.is_remove_noassign:
|
140 |
+
is_keeps = [False for i in range(len(target_ob.vertex_groups))]
|
141 |
+
for vert in target_me.vertices:
|
142 |
+
for vge in vert.groups:
|
143 |
+
if not is_keeps[vge.group]:
|
144 |
+
if 0.000001 < vge.weight:
|
145 |
+
is_keeps[vge.group] = True
|
146 |
+
copy_vertex_groups = target_ob.vertex_groups[:]
|
147 |
+
for i in range(len(copy_vertex_groups)):
|
148 |
+
if not is_keeps[i] and not copy_vertex_groups[i].lock_weight:
|
149 |
+
if copy_vertex_groups[i].name not in pre_vertex_group_names:
|
150 |
+
target_ob.vertex_groups.remove(copy_vertex_groups[i])
|
151 |
+
|
152 |
+
if self.is_target_select_vert_only:
|
153 |
+
for vert in target_me.vertices:
|
154 |
+
if not vert.select:
|
155 |
+
for vg in target_ob.vertex_groups:
|
156 |
+
vg.remove([vert.index])
|
157 |
+
for mvge in old_vertex_groups[vert.index]:
|
158 |
+
mvge.vertex_group.add([vert.index], mvge.weight, 'REPLACE')
|
159 |
+
|
160 |
+
for vert in target_me.vertices:
|
161 |
+
for vg in target_ob.vertex_groups:
|
162 |
+
if vg.lock_weight:
|
163 |
+
vg.remove([vert.index])
|
164 |
+
for mvge in old_vertex_groups[vert.index]:
|
165 |
+
if mvge.vertex_group.lock_weight:
|
166 |
+
mvge.vertex_group.add([vert.index], mvge.weight, 'REPLACE')
|
167 |
+
|
168 |
+
common.remove_data([temp_source_ob, temp_source_me])
|
169 |
+
join_source_ob.select = True
|
170 |
+
|
171 |
+
common.remove_data([join_source_ob, join_source_me])
|
172 |
+
for ob in original_source_obs:
|
173 |
+
ob.select = True
|
174 |
+
|
175 |
+
context.scene.objects.active = target_ob
|
176 |
+
bpy.ops.object.mode_set(mode=pre_mode)
|
177 |
+
return {'FINISHED'}
|
178 |
+
|
179 |
+
class precision_transfer_vertex_group(bpy.types.Operator):
|
180 |
+
bl_idname = 'object.precision_transfer_vertex_group'
|
181 |
+
bl_label = "High Precision Vertex Group transfer"
|
182 |
+
bl_description = "Will transfer the vertex groups from the previously selected mesh to the active mesh with more precision."
|
183 |
+
bl_options = {'REGISTER', 'UNDO'}
|
184 |
+
|
185 |
+
is_first_remove_all = bpy.props.BoolProperty(name="Remove Previous Groups", default=False)
|
186 |
+
subdivide_number = bpy.props.IntProperty(name="Subdivide Amount", default=1, min=0, max=10, soft_min=0, soft_max=10)
|
187 |
+
extend_range = bpy.props.FloatProperty(name="Range", default=1.1, min=1.0001, max=5.0, soft_min=1.0001, soft_max=5.0, step=10, precision=2)
|
188 |
+
is_remove_empty = bpy.props.BoolProperty(name="Remove Empty Vertex Groups", default=True)
|
189 |
+
|
190 |
+
@classmethod
|
191 |
+
def poll(cls, context):
|
192 |
+
active_ob = context.active_object
|
193 |
+
obs = context.selected_objects
|
194 |
+
if len(obs) != 2: return False
|
195 |
+
for ob in obs:
|
196 |
+
if ob.type != 'MESH':
|
197 |
+
return False
|
198 |
+
if ob.name != active_ob.name:
|
199 |
+
if len(ob.vertex_groups):
|
200 |
+
return True
|
201 |
+
return False
|
202 |
+
|
203 |
+
def invoke(self, context, event):
|
204 |
+
return context.window_manager.invoke_props_dialog(self)
|
205 |
+
|
206 |
+
def draw(self, context):
|
207 |
+
self.layout.prop(self, 'is_first_remove_all', icon='ERROR')
|
208 |
+
self.layout.prop(self, 'subdivide_number', icon='LATTICE_DATA')
|
209 |
+
self.layout.prop(self, 'extend_range', icon='META_EMPTY')
|
210 |
+
self.layout.prop(self, 'is_remove_empty', icon='X')
|
211 |
+
|
212 |
+
def execute(self, context):
|
213 |
+
import mathutils, time
|
214 |
+
start_time = time.time()
|
215 |
+
|
216 |
+
target_ob = context.active_object
|
217 |
+
target_me = target_ob.data
|
218 |
+
|
219 |
+
pre_mode = target_ob.mode
|
220 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
221 |
+
|
222 |
+
for ob in context.selected_objects:
|
223 |
+
if ob.name != target_ob.name:
|
224 |
+
source_original_ob = ob
|
225 |
+
break
|
226 |
+
source_ob = source_original_ob.copy()
|
227 |
+
source_me = source_original_ob.data.copy()
|
228 |
+
source_ob.data = source_me
|
229 |
+
context.scene.objects.link(source_ob)
|
230 |
+
context.scene.objects.active = source_ob
|
231 |
+
bpy.ops.object.mode_set(mode='EDIT')
|
232 |
+
bpy.ops.mesh.reveal()
|
233 |
+
bpy.ops.mesh.select_all(action='SELECT')
|
234 |
+
bpy.ops.mesh.subdivide(number_cuts=self.subdivide_number, smoothness=0.0, quadtri=False, quadcorner='STRAIGHT_CUT', fractal=0.0, fractal_along_normal=0.0, seed=0)
|
235 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
236 |
+
|
237 |
+
if self.is_first_remove_all:
|
238 |
+
for vg in target_ob.vertex_groups[:]:
|
239 |
+
if not vg.lock_weight:
|
240 |
+
target_ob.vertex_groups.remove(vg)
|
241 |
+
|
242 |
+
kd = mathutils.kdtree.KDTree(len(source_me.vertices))
|
243 |
+
for vert in source_me.vertices:
|
244 |
+
co = source_ob.matrix_world * vert.co
|
245 |
+
kd.insert(co, vert.index)
|
246 |
+
kd.balance()
|
247 |
+
|
248 |
+
context.window_manager.progress_begin(0, len(target_me.vertices))
|
249 |
+
progress_reduce = len(target_me.vertices) // 200 + 1
|
250 |
+
near_vert_data = []
|
251 |
+
near_vert_multi_total = []
|
252 |
+
near_vert_multi_total_append = near_vert_multi_total.append
|
253 |
+
for vert in target_me.vertices:
|
254 |
+
near_vert_data.append([])
|
255 |
+
near_vert_data_append = near_vert_data[-1].append
|
256 |
+
|
257 |
+
target_co = target_ob.matrix_world * vert.co
|
258 |
+
|
259 |
+
mini_co, mini_index, mini_dist = kd.find(target_co)
|
260 |
+
radius = mini_dist * self.extend_range
|
261 |
+
diff_radius = radius - mini_dist
|
262 |
+
|
263 |
+
multi_total = 0.0
|
264 |
+
for co, index, dist in kd.find_range(target_co, radius):
|
265 |
+
if 0 < diff_radius:
|
266 |
+
multi = (diff_radius - (dist - mini_dist)) / diff_radius
|
267 |
+
else:
|
268 |
+
multi = 1.0
|
269 |
+
near_vert_data_append((index, multi))
|
270 |
+
multi_total += multi
|
271 |
+
near_vert_multi_total_append(multi_total)
|
272 |
+
|
273 |
+
if vert.index % progress_reduce == 0:
|
274 |
+
context.window_manager.progress_update(vert.index)
|
275 |
+
context.window_manager.progress_end()
|
276 |
+
|
277 |
+
context.window_manager.progress_begin(0, len(source_ob.vertex_groups))
|
278 |
+
for source_vertex_group in source_ob.vertex_groups:
|
279 |
+
|
280 |
+
if source_vertex_group.name in target_ob.vertex_groups:
|
281 |
+
target_vertex_group = target_ob.vertex_groups[source_vertex_group.name]
|
282 |
+
else:
|
283 |
+
target_vertex_group = target_ob.vertex_groups.new(source_vertex_group.name)
|
284 |
+
|
285 |
+
is_waighted = False
|
286 |
+
|
287 |
+
source_weights = []
|
288 |
+
source_weights_append = source_weights.append
|
289 |
+
for source_vert in source_me.vertices:
|
290 |
+
for elem in source_vert.groups:
|
291 |
+
if elem.group == source_vertex_group.index:
|
292 |
+
source_weights_append(elem.weight)
|
293 |
+
break
|
294 |
+
else:
|
295 |
+
source_weights_append(0.0)
|
296 |
+
|
297 |
+
for target_vert in target_me.vertices:
|
298 |
+
|
299 |
+
if 0 < near_vert_multi_total[target_vert.index]:
|
300 |
+
|
301 |
+
total_weight = [source_weights[i] * m for i, m in near_vert_data[target_vert.index]]
|
302 |
+
total_weight = sum(total_weight)
|
303 |
+
|
304 |
+
average_weight = total_weight / near_vert_multi_total[target_vert.index]
|
305 |
+
else:
|
306 |
+
average_weight = 0.0
|
307 |
+
|
308 |
+
if 0.000001 < average_weight:
|
309 |
+
target_vertex_group.add([target_vert.index], average_weight, 'REPLACE')
|
310 |
+
is_waighted = True
|
311 |
+
else:
|
312 |
+
if not self.is_first_remove_all:
|
313 |
+
target_vertex_group.remove([target_vert.index])
|
314 |
+
|
315 |
+
context.window_manager.progress_update(source_vertex_group.index)
|
316 |
+
|
317 |
+
if not is_waighted and self.is_remove_empty:
|
318 |
+
target_ob.vertex_groups.remove(target_vertex_group)
|
319 |
+
context.window_manager.progress_end()
|
320 |
+
|
321 |
+
target_ob.vertex_groups.active_index = 0
|
322 |
+
|
323 |
+
common.remove_data([source_ob, source_me])
|
324 |
+
context.scene.objects.active = target_ob
|
325 |
+
bpy.ops.object.mode_set(mode=pre_mode)
|
326 |
+
|
327 |
+
diff_time = time.time() - start_time
|
328 |
+
self.report(type={'INFO'}, message=str(round(diff_time, 1)) + " Seconds")
|
329 |
+
return {'FINISHED'}
|
330 |
+
|
331 |
+
class quick_blur_vertex_group(bpy.types.Operator):
|
332 |
+
bl_idname = 'object.quick_blur_vertex_group'
|
333 |
+
bl_label = "Blur Vertex Group"
|
334 |
+
bl_description = "Will Blur the active vertex group or all vertex groups."
|
335 |
+
bl_options = {'REGISTER', 'UNDO'}
|
336 |
+
|
337 |
+
items = [
|
338 |
+
('ACTIVE', "Active", "", 'HAND', 1),
|
339 |
+
('ALL', "All", "", 'ARROW_LEFTRIGHT', 2),
|
340 |
+
]
|
341 |
+
target = bpy.props.EnumProperty(items=items, name="Target", default='ALL')
|
342 |
+
strength = bpy.props.FloatProperty(name="Strength", default=1.0, min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, step=10, precision=3)
|
343 |
+
count = bpy.props.IntProperty(name="Count", default=1, min=1, max=256, soft_min=1, soft_max=256)
|
344 |
+
size = bpy.props.FloatProperty(name="Size", default=0.0, min=-1.0, max=1.0, soft_min=-1.0, soft_max=1.0, step=10, precision=3)
|
345 |
+
|
346 |
+
@classmethod
|
347 |
+
def poll(cls, context):
|
348 |
+
ob = context.active_object
|
349 |
+
if ob:
|
350 |
+
if ob.type == 'MESH':
|
351 |
+
return ob.vertex_groups.active
|
352 |
+
return False
|
353 |
+
|
354 |
+
def invoke(self, context, event):
|
355 |
+
return context.window_manager.invoke_props_dialog(self)
|
356 |
+
|
357 |
+
def draw(self, context):
|
358 |
+
self.layout.prop(self, 'target', icon='VIEWZOOM')
|
359 |
+
self.layout.prop(self, 'strength')
|
360 |
+
self.layout.prop(self, 'count')
|
361 |
+
self.layout.prop(self, 'size')
|
362 |
+
|
363 |
+
def execute(self, context):
|
364 |
+
target_ob = context.active_object
|
365 |
+
target_me = target_ob.data
|
366 |
+
|
367 |
+
pre_mode = target_ob.mode
|
368 |
+
bpy.ops.object.mode_set(mode='WEIGHT_PAINT')
|
369 |
+
|
370 |
+
pre_use_paint_mask_vertex = target_me.use_paint_mask_vertex
|
371 |
+
target_me.use_paint_mask_vertex = True
|
372 |
+
|
373 |
+
bpy.ops.paint.vert_select_all(action='SELECT')
|
374 |
+
bpy.ops.object.vertex_group_smooth(group_select_mode=self.target, factor=self.strength, repeat=self.count, expand=self.size, source='ALL')
|
375 |
+
|
376 |
+
target_me.use_paint_mask_vertex = pre_use_paint_mask_vertex
|
377 |
+
|
378 |
+
bpy.ops.object.mode_set(mode=pre_mode)
|
379 |
+
return {'FINISHED'}
|
380 |
+
|
381 |
+
class blur_vertex_group(bpy.types.Operator):
|
382 |
+
bl_idname = 'object.blur_vertex_group'
|
383 |
+
bl_label = "Blur Vertex Group"
|
384 |
+
bl_description = "Blur or all just the active Vertex Group"
|
385 |
+
bl_options = {'REGISTER', 'UNDO'}
|
386 |
+
|
387 |
+
items = [
|
388 |
+
('ACTIVE', "Active", "", 'HAND', 1),
|
389 |
+
('UP', "Above Active", "", 'TRIA_UP_BAR', 2),
|
390 |
+
('DOWN', "Below Active", "", 'TRIA_DOWN_BAR', 3),
|
391 |
+
('ALL', "All", "", 'ARROW_LEFTRIGHT', 4),
|
392 |
+
]
|
393 |
+
target = bpy.props.EnumProperty(items=items, name="Target", default='ACTIVE')
|
394 |
+
radius = bpy.props.FloatProperty(name="Radius", default=3, min=0.1, max=50, soft_min=0.1, soft_max=50, step=50, precision=2)
|
395 |
+
strength = bpy.props.IntProperty(name="Strength", default=1, min=1, max=10, soft_min=1, soft_max=10)
|
396 |
+
items = [
|
397 |
+
('BOTH', "Both", "", 'AUTOMERGE_ON', 1),
|
398 |
+
('ADD', "Add", "", 'TRIA_UP', 2),
|
399 |
+
('SUB', "Subtract", "", 'TRIA_DOWN', 3),
|
400 |
+
]
|
401 |
+
effect = bpy.props.EnumProperty(items=items, name="Blur Effect", default='BOTH')
|
402 |
+
is_normalize = bpy.props.BoolProperty(name="Normalize", default=True)
|
403 |
+
|
404 |
+
@classmethod
|
405 |
+
def poll(cls, context):
|
406 |
+
ob = context.active_object
|
407 |
+
if ob:
|
408 |
+
if ob.type == 'MESH':
|
409 |
+
return ob.vertex_groups.active
|
410 |
+
return False
|
411 |
+
|
412 |
+
def invoke(self, context, event):
|
413 |
+
return context.window_manager.invoke_props_dialog(self)
|
414 |
+
|
415 |
+
def draw(self, context):
|
416 |
+
self.layout.prop(self, 'target', icon='VIEWZOOM')
|
417 |
+
self.layout.prop(self, 'radius', icon='META_EMPTY')
|
418 |
+
self.layout.prop(self, 'strength', icon='ARROW_LEFTRIGHT')
|
419 |
+
self.layout.prop(self, 'effect', icon='BRUSH_BLUR')
|
420 |
+
self.layout.prop(self, 'is_normalize', icon='ALIGN')
|
421 |
+
|
422 |
+
def execute(self, context):
|
423 |
+
import bmesh, mathutils
|
424 |
+
ob = context.active_object
|
425 |
+
me = ob.data
|
426 |
+
|
427 |
+
pre_mode = ob.mode
|
428 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
429 |
+
|
430 |
+
bm = bmesh.new()
|
431 |
+
bm.from_mesh(me)
|
432 |
+
edge_lengths = [e.calc_length() for e in bm.edges]
|
433 |
+
bm.free()
|
434 |
+
edge_lengths.sort()
|
435 |
+
average_edge_length = sum(edge_lengths) / len(edge_lengths)
|
436 |
+
center_index = int( (len(edge_lengths) - 1) / 2.0 )
|
437 |
+
average_edge_length = (average_edge_length + edge_lengths[center_index]) / 2
|
438 |
+
radius = average_edge_length * self.radius
|
439 |
+
|
440 |
+
context.window_manager.progress_begin(0, len(me.vertices))
|
441 |
+
progress_reduce = len(me.vertices) // 200 + 1
|
442 |
+
near_vert_data = []
|
443 |
+
kd = mathutils.kdtree.KDTree(len(me.vertices))
|
444 |
+
for vert in me.vertices:
|
445 |
+
kd.insert(vert.co.copy(), vert.index)
|
446 |
+
kd.balance()
|
447 |
+
for vert in me.vertices:
|
448 |
+
near_vert_data.append([])
|
449 |
+
near_vert_data_append = near_vert_data[-1].append
|
450 |
+
for co, index, dist in kd.find_range(vert.co, radius):
|
451 |
+
multi = (radius - dist) / radius
|
452 |
+
near_vert_data_append((index, multi))
|
453 |
+
if vert.index % progress_reduce == 0:
|
454 |
+
context.window_manager.progress_update(vert.index)
|
455 |
+
context.window_manager.progress_end()
|
456 |
+
|
457 |
+
target_vertex_groups = []
|
458 |
+
if self.target == 'ACTIVE':
|
459 |
+
target_vertex_groups.append(ob.vertex_groups.active)
|
460 |
+
elif self.target == 'UP':
|
461 |
+
for vertex_group in ob.vertex_groups:
|
462 |
+
if vertex_group.index <= ob.vertex_groups.active_index:
|
463 |
+
target_vertex_groups.append(vertex_group)
|
464 |
+
elif self.target == 'DOWN':
|
465 |
+
for vertex_group in ob.vertex_groups:
|
466 |
+
if ob.vertex_groups.active_index <= vertex_group.index:
|
467 |
+
target_vertex_groups.append(vertex_group)
|
468 |
+
elif self.target == 'ALL':
|
469 |
+
for vertex_group in ob.vertex_groups:
|
470 |
+
target_vertex_groups.append(vertex_group)
|
471 |
+
|
472 |
+
progress_total = len(target_vertex_groups) * self.strength * len(me.vertices)
|
473 |
+
context.window_manager.progress_begin(0, progress_total)
|
474 |
+
progress_reduce = progress_total // 200 + 1
|
475 |
+
progress_count = 0
|
476 |
+
for strength_count in range(self.strength):
|
477 |
+
for vertex_group in target_vertex_groups:
|
478 |
+
|
479 |
+
weights = []
|
480 |
+
weights_append = weights.append
|
481 |
+
for vert in me.vertices:
|
482 |
+
for elem in vert.groups:
|
483 |
+
if elem.group == vertex_group.index:
|
484 |
+
weights_append(elem.weight)
|
485 |
+
break
|
486 |
+
else:
|
487 |
+
weights_append(0.0)
|
488 |
+
|
489 |
+
for vert in me.vertices:
|
490 |
+
|
491 |
+
target_weight = weights[vert.index]
|
492 |
+
|
493 |
+
total_weight = 0.0
|
494 |
+
total_multi = 0.0
|
495 |
+
for index, multi in near_vert_data[vert.index]:
|
496 |
+
if self.effect == 'ADD':
|
497 |
+
if target_weight <= weights[index]:
|
498 |
+
total_weight += weights[index] * multi
|
499 |
+
total_multi += multi
|
500 |
+
elif self.effect == 'SUB':
|
501 |
+
if weights[index] <= target_weight:
|
502 |
+
total_weight += weights[index] * multi
|
503 |
+
total_multi += multi
|
504 |
+
else:
|
505 |
+
total_weight += weights[index] * multi
|
506 |
+
total_multi += multi
|
507 |
+
|
508 |
+
if 0 < total_multi:
|
509 |
+
average_weight = total_weight / total_multi
|
510 |
+
else:
|
511 |
+
average_weight = 0.0
|
512 |
+
|
513 |
+
if 0.001 < average_weight:
|
514 |
+
vertex_group.add([vert.index], average_weight, 'REPLACE')
|
515 |
+
else:
|
516 |
+
vertex_group.remove([vert.index])
|
517 |
+
|
518 |
+
progress_count += 1
|
519 |
+
if progress_count % progress_reduce == 0:
|
520 |
+
context.window_manager.progress_update(progress_count)
|
521 |
+
|
522 |
+
if self.is_normalize:
|
523 |
+
|
524 |
+
other_weight_total = 0.0
|
525 |
+
for elem in vert.groups:
|
526 |
+
if elem.group != vertex_group.index:
|
527 |
+
other_weight_total += elem.weight
|
528 |
+
|
529 |
+
diff_weight = average_weight - target_weight
|
530 |
+
new_other_weight_total = other_weight_total - diff_weight
|
531 |
+
if 0 < other_weight_total:
|
532 |
+
other_weight_multi = new_other_weight_total / other_weight_total
|
533 |
+
else:
|
534 |
+
other_weight_multi = 0.0
|
535 |
+
|
536 |
+
for elem in vert.groups:
|
537 |
+
if elem.group != vertex_group.index:
|
538 |
+
vg = ob.vertex_groups[elem.group]
|
539 |
+
vg.add([vert.index], elem.weight * other_weight_multi, 'REPLACE')
|
540 |
+
|
541 |
+
context.window_manager.progress_end()
|
542 |
+
bpy.ops.object.mode_set(mode=pre_mode)
|
543 |
+
return {'FINISHED'}
|
544 |
+
|
545 |
+
class multiply_vertex_group(bpy.types.Operator):
|
546 |
+
bl_idname = 'object.multiply_vertex_group'
|
547 |
+
bl_label = "Multiply vertex groups"
|
548 |
+
bl_description = "Multiply the weight of the vertex group by a numerical value to increase or decrease the weight strength"
|
549 |
+
bl_options = {'REGISTER', 'UNDO'}
|
550 |
+
|
551 |
+
items = [
|
552 |
+
('ACTIVE', "Active Only", "", 'HAND', 1),
|
553 |
+
('UP', "Above Active", "", 'TRIA_UP_BAR', 2),
|
554 |
+
('DOWN', "Below Active", "", 'TRIA_DOWN_BAR', 3),
|
555 |
+
('ALL', "All", "", 'ARROW_LEFTRIGHT', 4),
|
556 |
+
]
|
557 |
+
target = bpy.props.EnumProperty(items=items, name="Target", default='ACTIVE')
|
558 |
+
value = bpy.props.FloatProperty(name="Value", default=1.1, min=0.1, max=10, soft_min=0.1, soft_max=10, step=10, precision=2)
|
559 |
+
is_normalize = bpy.props.BoolProperty(name="Normalize", default=True)
|
560 |
+
|
561 |
+
@classmethod
|
562 |
+
def poll(cls, context):
|
563 |
+
ob = context.active_object
|
564 |
+
if ob:
|
565 |
+
if ob.type == 'MESH':
|
566 |
+
return ob.vertex_groups.active
|
567 |
+
return False
|
568 |
+
|
569 |
+
def invoke(self, context, event):
|
570 |
+
return context.window_manager.invoke_props_dialog(self)
|
571 |
+
|
572 |
+
def draw(self, context):
|
573 |
+
self.layout.prop(self, 'target', icon='VIEWZOOM')
|
574 |
+
self.layout.prop(self, 'value', icon='ARROW_LEFTRIGHT')
|
575 |
+
self.layout.prop(self, 'is_normalize', icon='ALIGN')
|
576 |
+
|
577 |
+
def execute(self, context):
|
578 |
+
ob = context.active_object
|
579 |
+
me = ob.data
|
580 |
+
|
581 |
+
pre_mode = ob.mode
|
582 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
583 |
+
|
584 |
+
target_vertex_groups = []
|
585 |
+
if self.target == 'ACTIVE':
|
586 |
+
target_vertex_groups.append(ob.vertex_groups.active)
|
587 |
+
elif self.target == 'UP':
|
588 |
+
for vertex_group in ob.vertex_groups:
|
589 |
+
if vertex_group.index <= ob.vertex_groups.active_index:
|
590 |
+
target_vertex_groups.append(vertex_group)
|
591 |
+
elif self.target == 'DOWN':
|
592 |
+
for vertex_group in ob.vertex_groups:
|
593 |
+
if ob.vertex_groups.active_index <= vertex_group.index:
|
594 |
+
target_vertex_groups.append(vertex_group)
|
595 |
+
elif self.target == 'ALL':
|
596 |
+
for vertex_group in ob.vertex_groups:
|
597 |
+
target_vertex_groups.append(vertex_group)
|
598 |
+
|
599 |
+
for vertex_group in target_vertex_groups:
|
600 |
+
for vert in me.vertices:
|
601 |
+
|
602 |
+
old_weight = -1
|
603 |
+
other_weight_total = 0.0
|
604 |
+
for elem in vert.groups:
|
605 |
+
if elem.group == vertex_group.index:
|
606 |
+
old_weight = elem.weight
|
607 |
+
else:
|
608 |
+
other_weight_total += elem.weight
|
609 |
+
if old_weight == -1:
|
610 |
+
continue
|
611 |
+
|
612 |
+
new_weight = old_weight * self.value
|
613 |
+
vertex_group.add([vert.index], new_weight, 'REPLACE')
|
614 |
+
|
615 |
+
if self.is_normalize:
|
616 |
+
|
617 |
+
diff_weight = new_weight - old_weight
|
618 |
+
|
619 |
+
new_other_weight_total = other_weight_total - diff_weight
|
620 |
+
if 0 < other_weight_total:
|
621 |
+
other_weight_multi = new_other_weight_total / other_weight_total
|
622 |
+
else:
|
623 |
+
other_weight_multi = 0.0
|
624 |
+
|
625 |
+
for elem in vert.groups:
|
626 |
+
if elem.group != vertex_group.index:
|
627 |
+
vg = ob.vertex_groups[elem.group]
|
628 |
+
vg.add([vert.index], elem.weight * other_weight_multi, 'REPLACE')
|
629 |
+
|
630 |
+
bpy.ops.object.mode_set(mode=pre_mode)
|
631 |
+
return {'FINISHED'}
|
632 |
+
|
633 |
+
class remove_noassign_vertex_groups(bpy.types.Operator):
|
634 |
+
bl_idname = 'object.remove_noassign_vertex_groups'
|
635 |
+
bl_label = "Delete Empty Vertex Groups"
|
636 |
+
bl_description = "Will delete any vertex groups which do not have any vertices assigned to it"
|
637 |
+
bl_options = {'REGISTER', 'UNDO'}
|
638 |
+
|
639 |
+
threshold = bpy.props.FloatProperty(name="Threshold", default=0.000001, min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, step=1, precision=10)
|
640 |
+
|
641 |
+
@classmethod
|
642 |
+
def poll(cls, context):
|
643 |
+
ob = context.active_object
|
644 |
+
if ob:
|
645 |
+
if ob.type == 'MESH':
|
646 |
+
return bool(len(ob.vertex_groups))
|
647 |
+
return False
|
648 |
+
|
649 |
+
def invoke(self, context, event):
|
650 |
+
return context.window_manager.invoke_props_dialog(self)
|
651 |
+
|
652 |
+
def draw(self, context):
|
653 |
+
self.layout.prop(self, 'threshold')
|
654 |
+
|
655 |
+
def execute(self, context):
|
656 |
+
ob = context.active_object
|
657 |
+
me = ob.data
|
658 |
+
|
659 |
+
is_keeps = [False for i in range(len(ob.vertex_groups))]
|
660 |
+
|
661 |
+
for vert in me.vertices:
|
662 |
+
for vge in vert.groups:
|
663 |
+
if not is_keeps[vge.group]:
|
664 |
+
if self.threshold < vge.weight:
|
665 |
+
is_keeps[vge.group] = True
|
666 |
+
|
667 |
+
copy_vertex_groups = ob.vertex_groups[:]
|
668 |
+
for i in range(len(copy_vertex_groups)):
|
669 |
+
if not is_keeps[i] and not copy_vertex_groups[i].lock_weight:
|
670 |
+
ob.vertex_groups.remove(copy_vertex_groups[i])
|
671 |
+
|
672 |
+
return {'FINISHED'}
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_OBJECT_PT_context_object.py
ADDED
@@ -0,0 +1,184 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 「プロパティ」エリア → 「オブジェクト」タブ
|
2 |
+
import os, re, sys, bpy, time, bmesh, mathutils
|
3 |
+
from . import common
|
4 |
+
|
5 |
+
# メニュー等に項目追加
|
6 |
+
def menu_func(self, context):
|
7 |
+
import re
|
8 |
+
ob = context.active_object
|
9 |
+
if not ob: return
|
10 |
+
if ob.type != 'MESH': return
|
11 |
+
|
12 |
+
bone_data_count = 0
|
13 |
+
if 'BoneData:0' in ob and 'LocalBoneData:0' in ob:
|
14 |
+
for key in ob.keys():
|
15 |
+
if re.search(r'^(Local)?BoneData:\d+$', key):
|
16 |
+
bone_data_count += 1
|
17 |
+
enabled_clipboard = False
|
18 |
+
clipboard = context.window_manager.clipboard
|
19 |
+
if 'BoneData:' in clipboard and 'LocalBoneData:' in clipboard:
|
20 |
+
enabled_clipboard = True
|
21 |
+
|
22 |
+
if bone_data_count or enabled_clipboard:
|
23 |
+
col = self.layout.column(align=True)
|
24 |
+
row = col.row(align=True)
|
25 |
+
row.label(text="CM3D2 Bone Data", icon_value=common.preview_collections['main']['KISS'].icon_id)
|
26 |
+
sub_row = row.row()
|
27 |
+
sub_row.alignment = 'RIGHT'
|
28 |
+
if 'BoneData:0' in ob and 'LocalBoneData:0' in ob:
|
29 |
+
bone_data_count = 0
|
30 |
+
for key in ob.keys():
|
31 |
+
if re.search(r'^(Local)?BoneData:\d+$', key):
|
32 |
+
bone_data_count += 1
|
33 |
+
sub_row.label(text=str(bone_data_count), icon='CHECKBOX_HLT')
|
34 |
+
else:
|
35 |
+
sub_row.label(text="0", icon='CHECKBOX_DEHLT')
|
36 |
+
row = col.row(align=True)
|
37 |
+
row.operator('object.copy_object_bone_data_property', icon='COPYDOWN', text="Copy")
|
38 |
+
row.operator('object.paste_object_bone_data_property', icon='PASTEDOWN', text="Paste")
|
39 |
+
row.operator('object.remove_object_bone_data_property', icon='X', text="")
|
40 |
+
|
41 |
+
class copy_object_bone_data_property(bpy.types.Operator):
|
42 |
+
bl_idname = 'object.copy_object_bone_data_property'
|
43 |
+
bl_label = "Copy the Bone Data from the object's custom properties"
|
44 |
+
bl_description = "Copies the bone Data in the object's custom properties to the clipboard."
|
45 |
+
bl_options = {'REGISTER', 'UNDO'}
|
46 |
+
|
47 |
+
@classmethod
|
48 |
+
def poll(cls, context):
|
49 |
+
ob = context.active_object
|
50 |
+
if ob:
|
51 |
+
if 'BoneData:0' in ob and 'LocalBoneData:0' in ob:
|
52 |
+
return True
|
53 |
+
return False
|
54 |
+
|
55 |
+
def execute(self, context):
|
56 |
+
output_text = ""
|
57 |
+
ob = context.active_object
|
58 |
+
pass_count = 0
|
59 |
+
if 'BaseBone' in ob:
|
60 |
+
output_text += "BaseBone:" + ob['BaseBone'] + "\n"
|
61 |
+
for i in range(99999):
|
62 |
+
name = "BoneData:" + str(i)
|
63 |
+
if name in ob:
|
64 |
+
output_text += "BoneData:" + ob[name] + "\n"
|
65 |
+
else:
|
66 |
+
pass_count += 1
|
67 |
+
if 10 < pass_count:
|
68 |
+
break
|
69 |
+
pass_count = 0
|
70 |
+
for i in range(99999):
|
71 |
+
name = "LocalBoneData:" + str(i)
|
72 |
+
if name in ob:
|
73 |
+
output_text += "LocalBoneData:" + ob[name] + "\n"
|
74 |
+
else:
|
75 |
+
pass_count += 1
|
76 |
+
if 10 < pass_count:
|
77 |
+
break
|
78 |
+
context.window_manager.clipboard = output_text
|
79 |
+
self.report(type={'INFO'}, message="Bone Data was copied to the clipboard.")
|
80 |
+
return {'FINISHED'}
|
81 |
+
|
82 |
+
class paste_object_bone_data_property(bpy.types.Operator):
|
83 |
+
bl_idname = 'object.paste_object_bone_data_property'
|
84 |
+
bl_label = "Paste Bone Data"
|
85 |
+
bl_description = "Paste Bone Data from the clipboard into the object's custom properties. NOTE:Any data in custom properties will be replaced."
|
86 |
+
bl_options = {'REGISTER', 'UNDO'}
|
87 |
+
|
88 |
+
@classmethod
|
89 |
+
def poll(cls, context):
|
90 |
+
ob = context.active_object
|
91 |
+
if ob:
|
92 |
+
clipboard = context.window_manager.clipboard
|
93 |
+
if 'BoneData:' in clipboard and 'LocalBoneData:' in clipboard:
|
94 |
+
return True
|
95 |
+
return False
|
96 |
+
|
97 |
+
def execute(self, context):
|
98 |
+
import re
|
99 |
+
ob = context.active_object
|
100 |
+
pass_count = 0
|
101 |
+
for i in range(99999):
|
102 |
+
name = "BoneData:" + str(i)
|
103 |
+
if name in ob:
|
104 |
+
del ob[name]
|
105 |
+
else:
|
106 |
+
pass_count += 1
|
107 |
+
if 10 < pass_count:
|
108 |
+
break
|
109 |
+
pass_count = 0
|
110 |
+
for i in range(99999):
|
111 |
+
name = "LocalBoneData:" + str(i)
|
112 |
+
if name in ob:
|
113 |
+
del ob[name]
|
114 |
+
else:
|
115 |
+
pass_count += 1
|
116 |
+
if 10 < pass_count:
|
117 |
+
break
|
118 |
+
bone_data_count = 0
|
119 |
+
local_bone_data_count = 0
|
120 |
+
for line in context.window_manager.clipboard.split("\n"):
|
121 |
+
r = re.search('^BaseBone:(.+)$', line)
|
122 |
+
if r:
|
123 |
+
ob['BaseBone'] = r.groups()[0]
|
124 |
+
r = re.search('^BoneData:(.+)$', line)
|
125 |
+
if r:
|
126 |
+
if line.count(',') == 4:
|
127 |
+
info = r.groups()[0]
|
128 |
+
name = "BoneData:" + str(bone_data_count)
|
129 |
+
ob[name] = info
|
130 |
+
bone_data_count += 1
|
131 |
+
r = re.search('^LocalBoneData:(.+)$', line)
|
132 |
+
if r:
|
133 |
+
if line.count(',') == 1:
|
134 |
+
info = r.groups()[0]
|
135 |
+
name = "LocalBoneData:" + str(local_bone_data_count)
|
136 |
+
ob[name] = info
|
137 |
+
local_bone_data_count += 1
|
138 |
+
self.report(type={'INFO'}, message="Data was pasted, mission accomplished")
|
139 |
+
return {'FINISHED'}
|
140 |
+
|
141 |
+
class remove_object_bone_data_property(bpy.types.Operator):
|
142 |
+
bl_idname = 'object.remove_object_bone_data_property'
|
143 |
+
bl_label = "Remove the bone Data"
|
144 |
+
bl_description = "Remove all bone Data for the custom properties"
|
145 |
+
bl_options = {'REGISTER', 'UNDO'}
|
146 |
+
|
147 |
+
@classmethod
|
148 |
+
def poll(cls, context):
|
149 |
+
ob = context.active_object
|
150 |
+
if ob:
|
151 |
+
if 'BoneData:0' in ob and 'LocalBoneData:0' in ob:
|
152 |
+
return True
|
153 |
+
return False
|
154 |
+
|
155 |
+
def invoke(self, context, event):
|
156 |
+
return context.window_manager.invoke_props_dialog(self)
|
157 |
+
|
158 |
+
def draw(self, context):
|
159 |
+
self.layout.label(text="Remove all bone Data form the custom properties?", icon='CANCEL')
|
160 |
+
|
161 |
+
def execute(self, context):
|
162 |
+
ob = context.active_object
|
163 |
+
pass_count = 0
|
164 |
+
if 'BaseBone' in ob:
|
165 |
+
del ob['BaseBone']
|
166 |
+
for i in range(99999):
|
167 |
+
name = "BoneData:" + str(i)
|
168 |
+
if name in ob:
|
169 |
+
del ob[name]
|
170 |
+
else:
|
171 |
+
pass_count += 1
|
172 |
+
if 10 < pass_count:
|
173 |
+
break
|
174 |
+
pass_count = 0
|
175 |
+
for i in range(99999):
|
176 |
+
name = "LocalBoneData:" + str(i)
|
177 |
+
if name in ob:
|
178 |
+
del ob[name]
|
179 |
+
else:
|
180 |
+
pass_count += 1
|
181 |
+
if 10 < pass_count:
|
182 |
+
break
|
183 |
+
self.report(type={'INFO'}, message="Bone Data was removed. Mission Accomplished.")
|
184 |
+
return {'FINISHED'}
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_OBJECT_PT_transform.py
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 「プロパティ」エリア → 「オブジェクト」タブ → 「トランスフォーム」パネル
|
2 |
+
import os, re, sys, bpy, time, bmesh, mathutils
|
3 |
+
from . import common
|
4 |
+
|
5 |
+
# メニュー等に項目追加
|
6 |
+
def menu_func(self, context):
|
7 |
+
self.layout.operator('object.sync_object_transform', icon_value=common.preview_collections['main']['KISS'].icon_id)
|
8 |
+
|
9 |
+
class sync_object_transform(bpy.types.Operator):
|
10 |
+
bl_idname = 'object.sync_object_transform'
|
11 |
+
bl_label = "Copy Origin Position"
|
12 |
+
bl_description = "The previously selected item's origin is copied onto the active object."
|
13 |
+
bl_options = {'REGISTER', 'UNDO'}
|
14 |
+
|
15 |
+
@classmethod
|
16 |
+
def poll(cls, context):
|
17 |
+
obs = context.selected_objects
|
18 |
+
return len(obs) == 2
|
19 |
+
|
20 |
+
def execute(self, context):
|
21 |
+
target_ob = context.active_object
|
22 |
+
for ob in context.selected_objects:
|
23 |
+
if target_ob.name != ob.name:
|
24 |
+
source_ob = ob
|
25 |
+
break
|
26 |
+
|
27 |
+
for area in context.screen.areas:
|
28 |
+
if area.type == 'VIEW_3D':
|
29 |
+
for space in area.spaces:
|
30 |
+
if space.type == 'VIEW_3D':
|
31 |
+
target_space = space
|
32 |
+
break
|
33 |
+
|
34 |
+
pre_cursor_location = target_space.cursor_location[:]
|
35 |
+
target_space.cursor_location = source_ob.location[:]
|
36 |
+
|
37 |
+
source_ob.select = False
|
38 |
+
bpy.ops.object.origin_set(type='ORIGIN_CURSOR')
|
39 |
+
source_ob.select = True
|
40 |
+
|
41 |
+
target_space.cursor_location = pre_cursor_location[:]
|
42 |
+
return {'FINISHED'}
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_RENDER_PT_bake.py
ADDED
@@ -0,0 +1,1680 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 「プロパティ」エリア → 「レンダー」タブ → 「ベイク」パネル
|
2 |
+
import os, re, sys, bpy, time, numpy, bmesh, mathutils
|
3 |
+
from . import common
|
4 |
+
|
5 |
+
# メニュー等に項目追加
|
6 |
+
def menu_func(self, context):
|
7 |
+
col = self.layout.column(align=True)
|
8 |
+
col.label(text="CM3D2 Bake", icon_value=common.preview_collections['main']['KISS'].icon_id)
|
9 |
+
row = col.row(align=True)
|
10 |
+
row.operator('object.add_bake_image', icon='IMAGE_COL', text="New image")
|
11 |
+
row.operator('object.quick_ao_bake_image', icon='BRUSH_TEXFILL', text="AO (Weight)")
|
12 |
+
row.operator('object.quick_dirty_bake_image', icon='MATSPHERE', text="Pseudo-AO")
|
13 |
+
row = col.row(align=True)
|
14 |
+
row.operator('object.quick_hemi_bake_image', icon='LAMP_HEMI', text="Hemi Lamp")
|
15 |
+
row.operator('object.quick_shadow_bake_image', icon='IMAGE_ALPHA', text="Shadow (Heavy)")
|
16 |
+
row.operator('object.quick_side_shadow_bake_image', icon='ARROW_LEFTRIGHT', text="Side Shade")
|
17 |
+
row = col.row(align=True)
|
18 |
+
row.operator('object.quick_gradation_bake_image', icon='MESH_PLANE', text="Gradation")
|
19 |
+
row.operator('object.quick_uv_border_bake_image', icon='MATCAP_24', text="UV Border")
|
20 |
+
row.operator('object.quick_mesh_border_bake_image', icon='EDIT_VEC', text="Mesh Edge")
|
21 |
+
row = col.row(align=True)
|
22 |
+
row.operator('object.quick_density_bake_image', icon='STICKY_UVS_LOC', text="Density")
|
23 |
+
row.operator('object.quick_bulge_bake_image', icon='BRUSH_INFLATE', text="Bulge")
|
24 |
+
row.operator('object.quick_mesh_distance_bake_image', icon='RETOPO', text="Mesh distance")
|
25 |
+
row = col.row(align=True)
|
26 |
+
row.operator('object.quick_metal_bake_image', icon='MATCAP_19', text="Metal")
|
27 |
+
row.operator('object.quick_hair_bake_image', icon='PARTICLEMODE', text="Hair")
|
28 |
+
row.operator('object.quick_semen_bake_image', icon='MOD_FLUIDSIM', text="Semen")
|
29 |
+
|
30 |
+
class add_bake_image(bpy.types.Operator):
|
31 |
+
bl_idname = 'object.add_bake_image'
|
32 |
+
bl_label = "Create an image for baking"
|
33 |
+
bl_description = "Prepares an empty image for baking in the active object"
|
34 |
+
bl_options = {'REGISTER', 'UNDO'}
|
35 |
+
|
36 |
+
image_name = bpy.props.StringProperty(name="Image Name")
|
37 |
+
items = [
|
38 |
+
('128', "128 px", "", 'LAYER_USED', 1),
|
39 |
+
('256', "256 px", "", 'LAYER_ACTIVE', 2),
|
40 |
+
('512', "512 px", "", 'HAND', 3),
|
41 |
+
('1024', "1024 px", "", 'FILE_TICK', 4),
|
42 |
+
('2048', "2048 px", "", 'ERROR', 5),
|
43 |
+
('4096', "4096 px", "", 'CANCEL', 6),
|
44 |
+
]
|
45 |
+
image_width = bpy.props.EnumProperty(items=items, name="Width", default='1024')
|
46 |
+
image_height = bpy.props.EnumProperty(items=items, name="Height", default='1024')
|
47 |
+
image_color = bpy.props.FloatVectorProperty(name="Color", default=(1, 1, 1, 1), min=0, max=1, soft_min=0, soft_max=1, step=10, precision=2, subtype='COLOR', size=4)
|
48 |
+
|
49 |
+
@classmethod
|
50 |
+
def poll(cls, context):
|
51 |
+
if len(context.selected_objects) != 1:
|
52 |
+
return False
|
53 |
+
ob = context.active_object
|
54 |
+
if ob:
|
55 |
+
if ob.type == 'MESH':
|
56 |
+
me = ob.data
|
57 |
+
if len(me.uv_layers):
|
58 |
+
return True
|
59 |
+
return False
|
60 |
+
|
61 |
+
def invoke(self, context, event):
|
62 |
+
ob = context.active_object
|
63 |
+
self.image_name = ob.name + " Bake"
|
64 |
+
return context.window_manager.invoke_props_dialog(self)
|
65 |
+
|
66 |
+
def draw(self, context):
|
67 |
+
self.layout.label(text="New image settings", icon='IMAGE_COL')
|
68 |
+
self.layout.prop(self, 'image_name', icon='SORTALPHA')
|
69 |
+
row = self.layout.row(align=True)
|
70 |
+
row.prop(self, 'image_width', icon='ARROW_LEFTRIGHT')
|
71 |
+
row.prop(self, 'image_height', icon='NLA_PUSHDOWN')
|
72 |
+
self.layout.prop(self, 'image_color', icon='COLOR')
|
73 |
+
|
74 |
+
def execute(self, context):
|
75 |
+
ob = context.active_object
|
76 |
+
me = ob.data
|
77 |
+
ob.hide_render = False
|
78 |
+
|
79 |
+
image_width, image_height = int(self.image_width), int(self.image_height)
|
80 |
+
|
81 |
+
if self.image_name in context.blend_data.images:
|
82 |
+
img = context.blend_data.images[self.image_name]
|
83 |
+
else:
|
84 |
+
img = context.blend_data.images.new(self.image_name, image_width, image_height, alpha=True)
|
85 |
+
|
86 |
+
area = common.get_request_area(context, 'IMAGE_EDITOR')
|
87 |
+
common.set_area_space_attr(area, 'image', img)
|
88 |
+
|
89 |
+
img.generated_color = self.image_color
|
90 |
+
|
91 |
+
for elem in me.uv_textures.active.data:
|
92 |
+
elem.image = img
|
93 |
+
|
94 |
+
return {'FINISHED'}
|
95 |
+
|
96 |
+
class quick_ao_bake_image(bpy.types.Operator):
|
97 |
+
bl_idname = 'object.quick_ao_bake_image'
|
98 |
+
bl_label = "AO Bake"
|
99 |
+
bl_description = "Quickly bake AO to active object"
|
100 |
+
bl_options = {'REGISTER', 'UNDO'}
|
101 |
+
|
102 |
+
image_name = bpy.props.StringProperty(name="Image Name")
|
103 |
+
items = [
|
104 |
+
('128', "128 px", "", 'LAYER_USED', 1),
|
105 |
+
('256', "256 px", "", 'LAYER_ACTIVE', 2),
|
106 |
+
('512', "512 px", "", 'HAND', 3),
|
107 |
+
('1024', "1024 px", "", 'FILE_TICK', 4),
|
108 |
+
('2048', "2048 px", "", 'ERROR', 5),
|
109 |
+
('4096', "4096 px", "", 'CANCEL', 6),
|
110 |
+
]
|
111 |
+
image_width = bpy.props.EnumProperty(items=items, name="Width", default='1024')
|
112 |
+
image_height = bpy.props.EnumProperty(items=items, name="Height", default='1024')
|
113 |
+
|
114 |
+
items = [
|
115 |
+
('RAYTRACE', "Ray Trace", "", 'BRUSH_TEXFILL', 1),
|
116 |
+
('APPROXIMATE', "Approximate(AAO)", "", 'MATSPHERE', 2),
|
117 |
+
]
|
118 |
+
ao_gather_method = bpy.props.EnumProperty(items=items, name="Gather method", default='RAYTRACE')
|
119 |
+
ao_samples = bpy.props.IntProperty(name="Accuracy", default=20, min=1, max=50, soft_min=1, soft_max=50)
|
120 |
+
ao_hide_other = bpy.props.BoolProperty(name="Hide other objects", default=True)
|
121 |
+
|
122 |
+
@classmethod
|
123 |
+
def poll(cls, context):
|
124 |
+
if len(context.selected_objects) != 1:
|
125 |
+
return False
|
126 |
+
ob = context.active_object
|
127 |
+
if ob:
|
128 |
+
if ob.type == 'MESH':
|
129 |
+
me = ob.data
|
130 |
+
if len(me.uv_layers):
|
131 |
+
return True
|
132 |
+
return False
|
133 |
+
|
134 |
+
def invoke(self, context, event):
|
135 |
+
ob = context.active_object
|
136 |
+
self.image_name = ob.name + " AO Bake"
|
137 |
+
return context.window_manager.invoke_props_dialog(self)
|
138 |
+
|
139 |
+
def draw(self, context):
|
140 |
+
self.layout.label(text="New image settings", icon='IMAGE_COL')
|
141 |
+
self.layout.prop(self, 'image_name', icon='SORTALPHA')
|
142 |
+
row = self.layout.row(align=True)
|
143 |
+
row.prop(self, 'image_width', icon='ARROW_LEFTRIGHT')
|
144 |
+
row.prop(self, 'image_height', icon='NLA_PUSHDOWN')
|
145 |
+
self.layout.label(text="AO Setting", icon='BRUSH_TEXFILL')
|
146 |
+
self.layout.prop(self, 'ao_gather_method', icon='NODETREE', expand=True)
|
147 |
+
self.layout.prop(self, 'ao_samples', icon='ANIM_DATA')
|
148 |
+
self.layout.prop(self, 'ao_hide_other', icon='VISIBLE_IPO_OFF')
|
149 |
+
|
150 |
+
def execute(self, context):
|
151 |
+
ob = context.active_object
|
152 |
+
me = ob.data
|
153 |
+
ob.hide_render = False
|
154 |
+
|
155 |
+
image_width, image_height = int(self.image_width), int(self.image_height)
|
156 |
+
|
157 |
+
if self.image_name in context.blend_data.images:
|
158 |
+
img = context.blend_data.images[self.image_name]
|
159 |
+
else:
|
160 |
+
img = context.blend_data.images.new(self.image_name, image_width, image_height, alpha=True)
|
161 |
+
|
162 |
+
area = common.get_request_area(context, 'IMAGE_EDITOR')
|
163 |
+
common.set_area_space_attr(area, 'image', img)
|
164 |
+
|
165 |
+
for elem in me.uv_textures.active.data:
|
166 |
+
elem.image = img
|
167 |
+
|
168 |
+
context.scene.world.light_settings.gather_method = self.ao_gather_method
|
169 |
+
context.scene.world.light_settings.samples = self.ao_samples
|
170 |
+
|
171 |
+
if self.ao_hide_other: hide_render_restore = common.hide_render_restore()
|
172 |
+
|
173 |
+
context.scene.render.bake_type = 'AO'
|
174 |
+
context.scene.render.use_bake_normalize = True
|
175 |
+
context.scene.render.use_bake_selected_to_active = False
|
176 |
+
bpy.ops.object.bake_image()
|
177 |
+
|
178 |
+
if self.ao_hide_other: hide_render_restore.restore()
|
179 |
+
|
180 |
+
return {'FINISHED'}
|
181 |
+
|
182 |
+
class quick_dirty_bake_image(bpy.types.Operator):
|
183 |
+
bl_idname = 'object.quick_dirty_bake_image'
|
184 |
+
bl_label = "Pseudo AO bake"
|
185 |
+
bl_description = "bake a quick psuedo AO in the active object"
|
186 |
+
bl_options = {'REGISTER', 'UNDO'}
|
187 |
+
|
188 |
+
image_name = bpy.props.StringProperty(name="Image name")
|
189 |
+
items = [
|
190 |
+
('128', "128 px", "", 'LAYER_USED', 1),
|
191 |
+
('256', "256 px", "", 'LAYER_ACTIVE', 2),
|
192 |
+
('512', "512 px", "", 'HAND', 3),
|
193 |
+
('1024', "1024 px", "", 'FILE_TICK', 4),
|
194 |
+
('2048', "2048 px", "", 'ERROR', 5),
|
195 |
+
('4096', "4096 px", "", 'CANCEL', 6),
|
196 |
+
]
|
197 |
+
image_width = bpy.props.EnumProperty(items=items, name="Width", default='1024')
|
198 |
+
image_height = bpy.props.EnumProperty(items=items, name="Height", default='1024')
|
199 |
+
|
200 |
+
blur_strength = bpy.props.FloatProperty(name="Blur strength", default=1, min=0.01, max=1, soft_min=0.01, soft_max=1, step=10, precision=2)
|
201 |
+
blur_iterations = bpy.props.IntProperty(name="Blur iterations", default=1, min=0, max=40, soft_min=0, soft_max=40)
|
202 |
+
clean_angle = bpy.props.FloatProperty(name="Highlight angles", default=3.14159, min=0, max=3.14159, soft_min=0, soft_max=3.14159, step=3, precision=0, subtype='ANGLE')
|
203 |
+
dirt_angle = bpy.props.FloatProperty(name="Pseudo AO Angle", default=0, min=0, max=3.14159, soft_min=0, soft_max=3.14159, step=3, precision=0, subtype='ANGLE')
|
204 |
+
dirt_only = bpy.props.BoolProperty(name="Pseudo AO only", default=True)
|
205 |
+
|
206 |
+
@classmethod
|
207 |
+
def poll(cls, context):
|
208 |
+
if len(context.selected_objects) != 1:
|
209 |
+
return False
|
210 |
+
ob = context.active_object
|
211 |
+
if ob:
|
212 |
+
if ob.type == 'MESH':
|
213 |
+
me = ob.data
|
214 |
+
if len(me.uv_layers):
|
215 |
+
return True
|
216 |
+
return False
|
217 |
+
|
218 |
+
def invoke(self, context, event):
|
219 |
+
ob = context.active_object
|
220 |
+
self.image_name = ob.name + " Dirty AO Bake"
|
221 |
+
return context.window_manager.invoke_props_dialog(self)
|
222 |
+
|
223 |
+
def draw(self, context):
|
224 |
+
self.layout.label(text="New image settings", icon='IMAGE_COL')
|
225 |
+
self.layout.prop(self, 'image_name', icon='SORTALPHA')
|
226 |
+
row = self.layout.row(align=True)
|
227 |
+
row.prop(self, 'image_width', icon='ARROW_LEFTRIGHT')
|
228 |
+
row.prop(self, 'image_height', icon='NLA_PUSHDOWN')
|
229 |
+
self.layout.label(text="Pseudo AO Setting", icon='BRUSH_TEXFILL')
|
230 |
+
row = self.layout.row(align=True)
|
231 |
+
row.prop(self, 'blur_strength', icon='NONE', slider=True)
|
232 |
+
row.prop(self, 'blur_iterations', icon='NONE')
|
233 |
+
self.layout.prop(self, 'clean_angle', icon='NONE', slider=True)
|
234 |
+
row = self.layout.row(align=True)
|
235 |
+
row.prop(self, 'dirt_angle', icon='NONE', slider=True)
|
236 |
+
row.prop(self, 'dirt_only', icon='FILE_TICK')
|
237 |
+
|
238 |
+
def execute(self, context):
|
239 |
+
ob = context.active_object
|
240 |
+
me = ob.data
|
241 |
+
ob.select = False
|
242 |
+
ob.hide_render = False
|
243 |
+
|
244 |
+
image_width, image_height = int(self.image_width), int(self.image_height)
|
245 |
+
|
246 |
+
if self.image_name in context.blend_data.images:
|
247 |
+
img = context.blend_data.images[self.image_name]
|
248 |
+
else:
|
249 |
+
img = context.blend_data.images.new(self.image_name, image_width, image_height, alpha=True)
|
250 |
+
|
251 |
+
area = common.get_request_area(context, 'IMAGE_EDITOR')
|
252 |
+
common.set_area_space_attr(area, 'image', img)
|
253 |
+
for elem in me.uv_textures.active.data:
|
254 |
+
elem.image = img
|
255 |
+
|
256 |
+
temp_me = ob.to_mesh(scene=context.scene, apply_modifiers=True, settings='PREVIEW')
|
257 |
+
temp_ob = context.blend_data.objects.new("quick_dirty_bake_image_temp", temp_me)
|
258 |
+
context.scene.objects.link(temp_ob)
|
259 |
+
for vc in temp_me.vertex_colors:
|
260 |
+
temp_me.vertex_colors.remove(vc)
|
261 |
+
temp_vertex_color = temp_me.vertex_colors.new(name="quick_dirty_bake_image_temp")
|
262 |
+
context.scene.objects.active = temp_ob
|
263 |
+
temp_ob.select = True
|
264 |
+
|
265 |
+
override = context.copy()
|
266 |
+
override['object'] = temp_ob
|
267 |
+
bpy.ops.paint.vertex_color_dirt(override, blur_strength=self.blur_strength, blur_iterations=self.blur_iterations, clean_angle=self.clean_angle, dirt_angle=self.dirt_angle, dirt_only=self.dirt_only)
|
268 |
+
|
269 |
+
temp_ob.update_tag(refresh={'OBJECT', 'DATA'})
|
270 |
+
context.scene.render.bake_type = 'VERTEX_COLORS'
|
271 |
+
context.scene.render.use_bake_selected_to_active = False
|
272 |
+
bpy.ops.object.bake_image(context.copy())
|
273 |
+
|
274 |
+
common.remove_data([temp_me, temp_ob])
|
275 |
+
context.scene.objects.active = ob
|
276 |
+
ob.select = True
|
277 |
+
|
278 |
+
return {'FINISHED'}
|
279 |
+
|
280 |
+
class quick_hemi_bake_image(bpy.types.Operator):
|
281 |
+
bl_idname = 'object.quick_hemi_bake_image'
|
282 |
+
bl_label = "Hemi lamp bake"
|
283 |
+
bl_description = "Bake the shadow of a hemi lamp"
|
284 |
+
bl_options = {'REGISTER', 'UNDO'}
|
285 |
+
|
286 |
+
image_name = bpy.props.StringProperty(name="Image name")
|
287 |
+
items = [
|
288 |
+
('128', "128 px", "", 'LAYER_USED', 1),
|
289 |
+
('256', "256 px", "", 'LAYER_ACTIVE', 2),
|
290 |
+
('512', "512 px", "", 'HAND', 3),
|
291 |
+
('1024', "1024 px", "", 'FILE_TICK', 4),
|
292 |
+
('2048', "2048 px", "", 'ERROR', 5),
|
293 |
+
('4096', "4096 px", "", 'CANCEL', 6),
|
294 |
+
]
|
295 |
+
image_width = bpy.props.EnumProperty(items=items, name="Width", default='1024')
|
296 |
+
image_height = bpy.props.EnumProperty(items=items, name="Height", default='1024')
|
297 |
+
|
298 |
+
lamp_energy = bpy.props.FloatProperty(name="Light intensity", default=1, min=0, max=2, soft_min=0, soft_max=2, step=50, precision=2)
|
299 |
+
|
300 |
+
use_ao = bpy.props.BoolProperty(name="Use the AO", default=False)
|
301 |
+
ao_samples = bpy.props.IntProperty(name="Accuracy", default=20, min=1, max=50, soft_min=1, soft_max=50)
|
302 |
+
ao_hide_other = bpy.props.BoolProperty(name="Hide other objects", default=True)
|
303 |
+
|
304 |
+
@classmethod
|
305 |
+
def poll(cls, context):
|
306 |
+
if len(context.selected_objects) != 1:
|
307 |
+
return False
|
308 |
+
ob = context.active_object
|
309 |
+
if ob:
|
310 |
+
if ob.type == 'MESH':
|
311 |
+
me = ob.data
|
312 |
+
if len(me.uv_layers):
|
313 |
+
return True
|
314 |
+
return False
|
315 |
+
|
316 |
+
def invoke(self, context, event):
|
317 |
+
ob = context.active_object
|
318 |
+
self.image_name = ob.name + " Hemi Bake"
|
319 |
+
return context.window_manager.invoke_props_dialog(self)
|
320 |
+
|
321 |
+
def draw(self, context):
|
322 |
+
self.layout.label(text="New image settings", icon='IMAGE_COL')
|
323 |
+
self.layout.prop(self, 'image_name', icon='SORTALPHA')
|
324 |
+
row = self.layout.row(align=True)
|
325 |
+
row.prop(self, 'image_width', icon='ARROW_LEFTRIGHT')
|
326 |
+
row.prop(self, 'image_height', icon='NLA_PUSHDOWN')
|
327 |
+
self.layout.label(text="Hemi lamp settings", icon='LAMP_HEMI')
|
328 |
+
self.layout.prop(self, 'lamp_energy', icon='LAMP_POINT', slider=True)
|
329 |
+
self.layout.label(text="AO setting", icon='BRUSH_TEXFILL')
|
330 |
+
row = self.layout.row(align=True)
|
331 |
+
row.prop(self, 'use_ao', icon='FILE_TICK')
|
332 |
+
row.prop(self, 'ao_samples', icon='ANIM_DATA')
|
333 |
+
self.layout.prop(self, 'ao_hide_other', icon='VISIBLE_IPO_OFF')
|
334 |
+
|
335 |
+
def execute(self, context):
|
336 |
+
ob = context.active_object
|
337 |
+
me = ob.data
|
338 |
+
ob.hide_render = False
|
339 |
+
|
340 |
+
override = context.copy()
|
341 |
+
override['object'] = ob
|
342 |
+
|
343 |
+
image_width, image_height = int(self.image_width), int(self.image_height)
|
344 |
+
|
345 |
+
if self.image_name in context.blend_data.images:
|
346 |
+
img = context.blend_data.images[self.image_name]
|
347 |
+
else:
|
348 |
+
img = context.blend_data.images.new(self.image_name, image_width, image_height, alpha=True)
|
349 |
+
|
350 |
+
area = common.get_request_area(context, 'IMAGE_EDITOR')
|
351 |
+
common.set_area_space_attr(area, 'image', img)
|
352 |
+
for elem in me.uv_textures.active.data:
|
353 |
+
elem.image = img
|
354 |
+
|
355 |
+
if self.ao_hide_other: hide_render_restore = common.hide_render_restore()
|
356 |
+
material_restore = common.material_restore(ob)
|
357 |
+
|
358 |
+
bpy.ops.object.material_slot_add(override)
|
359 |
+
temp_mate = context.blend_data.materials.new("quick_hemi_bake_image_temp")
|
360 |
+
ob.material_slots[0].material = temp_mate
|
361 |
+
temp_mate.diffuse_intensity = 1.0
|
362 |
+
temp_mate.diffuse_color = (1, 1, 1)
|
363 |
+
|
364 |
+
temp_lamp = context.blend_data.lamps.new("quick_hemi_bake_image_temp", 'HEMI')
|
365 |
+
temp_ob = context.blend_data.objects.new("quick_hemi_bake_image_temp", temp_lamp)
|
366 |
+
context.scene.objects.link(temp_ob)
|
367 |
+
temp_lamp.energy = self.lamp_energy
|
368 |
+
|
369 |
+
context.scene.world.light_settings.use_ambient_occlusion = self.use_ao
|
370 |
+
if self.use_ao:
|
371 |
+
context.scene.world.light_settings.samples = self.ao_samples
|
372 |
+
context.scene.world.light_settings.ao_blend_type = 'MULTIPLY'
|
373 |
+
|
374 |
+
context.scene.render.bake_type = 'FULL'
|
375 |
+
context.scene.render.use_bake_selected_to_active = False
|
376 |
+
bpy.ops.object.bake_image()
|
377 |
+
|
378 |
+
common.remove_data([temp_lamp, temp_ob, temp_mate])
|
379 |
+
|
380 |
+
material_restore.restore()
|
381 |
+
if self.ao_hide_other: hide_render_restore.restore()
|
382 |
+
|
383 |
+
return {'FINISHED'}
|
384 |
+
|
385 |
+
class quick_shadow_bake_image(bpy.types.Operator):
|
386 |
+
bl_idname = 'object.quick_shadow_bake_image'
|
387 |
+
bl_label = "Shadow Bake"
|
388 |
+
bl_description = "Quickly bake a shadow on the active object"
|
389 |
+
bl_options = {'REGISTER', 'UNDO'}
|
390 |
+
|
391 |
+
image_name = bpy.props.StringProperty(name="Image name")
|
392 |
+
items = [
|
393 |
+
('128', "128 px", "", 'LAYER_USED', 1),
|
394 |
+
('256', "256 px", "", 'LAYER_ACTIVE', 2),
|
395 |
+
('512', "512 px", "", 'HAND', 3),
|
396 |
+
('1024', "1024 px", "", 'FILE_TICK', 4),
|
397 |
+
('2048', "2048 px", "", 'ERROR', 5),
|
398 |
+
('4096', "4096 px", "", 'CANCEL', 6),
|
399 |
+
]
|
400 |
+
image_width = bpy.props.EnumProperty(items=items, name="Width", default='1024')
|
401 |
+
image_height = bpy.props.EnumProperty(items=items, name="Height", default='1024')
|
402 |
+
|
403 |
+
lamp_max_angle = bpy.props.FloatProperty(name="Lamp max angle", default=0.5236, min=0, max=1.5708, soft_min=0, soft_max=1.5708, step=100, precision=0, subtype='ANGLE', unit='ROTATION')
|
404 |
+
lamp_count = bpy.props.IntProperty(name="Number of light sources", default=8, min=1, max=20, soft_min=1, soft_max=20)
|
405 |
+
is_shadow_only = bpy.props.BoolProperty(name="Shadow focus", default=False)
|
406 |
+
|
407 |
+
@classmethod
|
408 |
+
def poll(cls, context):
|
409 |
+
if not len(context.selected_objects):
|
410 |
+
return False
|
411 |
+
ob = context.active_object
|
412 |
+
if ob:
|
413 |
+
if ob.type == 'MESH':
|
414 |
+
me = ob.data
|
415 |
+
if len(me.uv_layers):
|
416 |
+
return True
|
417 |
+
return False
|
418 |
+
|
419 |
+
def invoke(self, context, event):
|
420 |
+
self.image_name = context.active_object.name + " Shadow Bake"
|
421 |
+
return context.window_manager.invoke_props_dialog(self)
|
422 |
+
|
423 |
+
def draw(self, context):
|
424 |
+
self.layout.label(text="New image settings", icon='IMAGE_COL')
|
425 |
+
self.layout.prop(self, 'image_name', icon='SORTALPHA')
|
426 |
+
row = self.layout.row(align=True)
|
427 |
+
row.prop(self, 'image_width', icon='ARROW_LEFTRIGHT')
|
428 |
+
row.prop(self, 'image_height', icon='NLA_PUSHDOWN')
|
429 |
+
self.layout.label(text="Light source setting", icon='LAMP_SUN')
|
430 |
+
self.layout.prop(self, 'lamp_max_angle', icon='LAMP_AREA', slider=True)
|
431 |
+
self.layout.prop(self, 'lamp_count', icon='LAMP_POINT')
|
432 |
+
self.layout.prop(self, 'is_shadow_only', icon='IMAGE_ALPHA')
|
433 |
+
|
434 |
+
def execute(self, context):
|
435 |
+
import mathutils
|
436 |
+
|
437 |
+
ob = context.active_object
|
438 |
+
me = ob.data
|
439 |
+
ob.hide_render = False
|
440 |
+
|
441 |
+
override = context.copy()
|
442 |
+
override['object'] = ob
|
443 |
+
|
444 |
+
image_width, image_height = int(self.image_width), int(self.image_height)
|
445 |
+
|
446 |
+
if self.image_name in context.blend_data.images:
|
447 |
+
img = context.blend_data.images[self.image_name]
|
448 |
+
else:
|
449 |
+
img = context.blend_data.images.new(self.image_name, image_width, image_height, alpha=True)
|
450 |
+
|
451 |
+
area = common.get_request_area(context, 'IMAGE_EDITOR')
|
452 |
+
common.set_area_space_attr(area, 'image', img)
|
453 |
+
for elem in me.uv_textures.active.data:
|
454 |
+
elem.image = img
|
455 |
+
|
456 |
+
hide_render_restore = common.hide_render_restore()
|
457 |
+
material_restore = common.material_restore(ob)
|
458 |
+
|
459 |
+
bpy.ops.object.material_slot_add(override)
|
460 |
+
temp_mate = context.blend_data.materials.new("quick_shadow_bake_image_temp")
|
461 |
+
ob.material_slots[0].material = temp_mate
|
462 |
+
|
463 |
+
if self.is_shadow_only:
|
464 |
+
temp_hemi = context.blend_data.lamps.new("quick_hemi_bake_image_lamp_temp", 'HEMI')
|
465 |
+
temp_hemi_ob = context.blend_data.objects.new("quick_hemi_bake_image_lamp_temp", temp_hemi)
|
466 |
+
context.scene.objects.link(temp_hemi_ob)
|
467 |
+
temp_hemi.energy = 0.00001
|
468 |
+
|
469 |
+
new_lamps = []
|
470 |
+
lamp_count = (self.lamp_count * 2) - 1
|
471 |
+
angle_interval = self.lamp_max_angle / self.lamp_count
|
472 |
+
for x_index in range(lamp_count):
|
473 |
+
x_angle = angle_interval * (x_index - self.lamp_count + 1)
|
474 |
+
|
475 |
+
for y_index in range(lamp_count):
|
476 |
+
y_angle = angle_interval * (y_index - self.lamp_count + 1)
|
477 |
+
|
478 |
+
temp_lamp = context.blend_data.lamps.new("quick_shadow_bake_image_temp", 'SUN')
|
479 |
+
temp_lamp.shadow_method = 'RAY_SHADOW'
|
480 |
+
temp_lamp_ob = context.blend_data.objects.new("quick_shadow_bake_image_temp", temp_lamp)
|
481 |
+
context.scene.objects.link(temp_lamp_ob)
|
482 |
+
temp_lamp_ob.rotation_mode = 'XYZ'
|
483 |
+
temp_lamp_ob.rotation_euler = mathutils.Euler((x_angle, y_angle, 0), 'XYZ')
|
484 |
+
|
485 |
+
new_lamps.append(temp_lamp)
|
486 |
+
new_lamps.append(temp_lamp_ob)
|
487 |
+
|
488 |
+
context.scene.render.bake_type = 'SHADOW'
|
489 |
+
context.scene.render.use_bake_selected_to_active = False
|
490 |
+
bpy.ops.object.bake_image()
|
491 |
+
|
492 |
+
common.remove_data([temp_mate] + new_lamps)
|
493 |
+
if self.is_shadow_only: common.remove_data([temp_hemi_ob, temp_hemi])
|
494 |
+
|
495 |
+
material_restore.restore()
|
496 |
+
hide_render_restore.restore()
|
497 |
+
|
498 |
+
return {'FINISHED'}
|
499 |
+
|
500 |
+
class quick_side_shadow_bake_image(bpy.types.Operator):
|
501 |
+
bl_idname = 'object.quick_side_shadow_bake_image'
|
502 |
+
bl_label = "Side shadow bake"
|
503 |
+
bl_description = "A quick side shadow is baked on the active object"
|
504 |
+
bl_options = {'REGISTER', 'UNDO'}
|
505 |
+
|
506 |
+
image_name = bpy.props.StringProperty(name="Image name")
|
507 |
+
items = [
|
508 |
+
('128', "128 px", "", 'LAYER_USED', 1),
|
509 |
+
('256', "256 px", "", 'LAYER_ACTIVE', 2),
|
510 |
+
('512', "512 px", "", 'HAND', 3),
|
511 |
+
('1024', "1024 px", "", 'FILE_TICK', 4),
|
512 |
+
('2048', "2048 px", "", 'ERROR', 5),
|
513 |
+
('4096', "4096 px", "", 'CANCEL', 6),
|
514 |
+
]
|
515 |
+
image_width = bpy.props.EnumProperty(items=items, name="Width", default='1024')
|
516 |
+
image_height = bpy.props.EnumProperty(items=items, name="Height", default='1024')
|
517 |
+
|
518 |
+
is_bipolarization = bpy.props.BoolProperty(name="Enable polarization", default=True)
|
519 |
+
bipolarization_threshold = bpy.props.FloatProperty(name="Threshold of polarization", default=0.5, min=0, max=1, soft_min=0, soft_max=1, step=5, precision=2)
|
520 |
+
bipolarization_blur = bpy.props.FloatProperty(name="Blur of polarization", default=0.05, min=0, max=1, soft_min=0, soft_max=1, step=1, precision=2)
|
521 |
+
|
522 |
+
@classmethod
|
523 |
+
def poll(cls, context):
|
524 |
+
if len(context.selected_objects) != 1:
|
525 |
+
return False
|
526 |
+
ob = context.active_object
|
527 |
+
if ob:
|
528 |
+
if ob.type == 'MESH':
|
529 |
+
me = ob.data
|
530 |
+
if len(me.uv_layers):
|
531 |
+
return True
|
532 |
+
return False
|
533 |
+
|
534 |
+
def invoke(self, context, event):
|
535 |
+
self.image_name = context.active_object.name + " SideShade Bake"
|
536 |
+
return context.window_manager.invoke_props_dialog(self)
|
537 |
+
|
538 |
+
def draw(self, context):
|
539 |
+
self.layout.label(text="New image settings", icon='IMAGE_COL')
|
540 |
+
self.layout.prop(self, 'image_name', icon='SORTALPHA')
|
541 |
+
row = self.layout.row(align=True)
|
542 |
+
row.prop(self, 'image_width', icon='ARROW_LEFTRIGHT')
|
543 |
+
row.prop(self, 'image_height', icon='NLA_PUSHDOWN')
|
544 |
+
self.layout.separator()
|
545 |
+
self.layout.prop(self, 'is_bipolarization', icon='IMAGE_ALPHA')
|
546 |
+
row = self.layout.row(align=True)
|
547 |
+
row.prop(self, 'bipolarization_threshold', icon='NONE', text="Threshold")
|
548 |
+
row.prop(self, 'bipolarization_blur', icon='NONE', text="Blur")
|
549 |
+
|
550 |
+
def execute(self, context):
|
551 |
+
ob = context.active_object
|
552 |
+
me = ob.data
|
553 |
+
ob.hide_render = False
|
554 |
+
|
555 |
+
override = context.copy()
|
556 |
+
override['object'] = ob
|
557 |
+
|
558 |
+
image_width, image_height = int(self.image_width), int(self.image_height)
|
559 |
+
|
560 |
+
if self.image_name in context.blend_data.images:
|
561 |
+
img = context.blend_data.images[self.image_name]
|
562 |
+
else:
|
563 |
+
img = context.blend_data.images.new(self.image_name, image_width, image_height, alpha=True, float_buffer=True)
|
564 |
+
|
565 |
+
area = common.get_request_area(context, 'IMAGE_EDITOR')
|
566 |
+
common.set_area_space_attr(area, 'image', img)
|
567 |
+
for elem in me.uv_textures.active.data:
|
568 |
+
elem.image = img
|
569 |
+
|
570 |
+
material_restore = common.material_restore(ob)
|
571 |
+
|
572 |
+
blend_path = os.path.join(os.path.dirname(__file__), "append_data.blend")
|
573 |
+
with context.blend_data.libraries.load(blend_path) as (data_from, data_to):
|
574 |
+
data_to.materials = ["Side Shadow"]
|
575 |
+
|
576 |
+
bpy.ops.object.material_slot_add(override)
|
577 |
+
temp_mate = data_to.materials[0]
|
578 |
+
ob.material_slots[0].material = temp_mate
|
579 |
+
|
580 |
+
temp_lamp = context.blend_data.lamps.new("quick_side_shadow_bake_image_lamp_temp", 'HEMI')
|
581 |
+
temp_lamp_ob = context.blend_data.objects.new("quick_side_shadow_bake_image_lamp_temp", temp_lamp)
|
582 |
+
context.scene.objects.link(temp_lamp_ob)
|
583 |
+
|
584 |
+
pre_scene_camera = context.scene.camera
|
585 |
+
temp_camera = context.blend_data.cameras.new("quick_side_shadow_bake_image_camera_temp")
|
586 |
+
temp_camera_ob = context.blend_data.objects.new("quick_side_shadow_bake_image_camera_temp", temp_camera)
|
587 |
+
context.scene.objects.link(temp_camera_ob)
|
588 |
+
temp_camera_ob.rotation_euler[0] = 1.5708
|
589 |
+
context.scene.camera = temp_camera_ob
|
590 |
+
|
591 |
+
context.scene.world.light_settings.use_ambient_occlusion = False
|
592 |
+
|
593 |
+
context.scene.render.bake_type = 'FULL'
|
594 |
+
context.scene.render.use_bake_selected_to_active = False
|
595 |
+
bpy.ops.object.bake_image()
|
596 |
+
|
597 |
+
common.remove_data([temp_mate, temp_lamp_ob, temp_lamp, temp_camera_ob, temp_camera])
|
598 |
+
context.scene.camera = pre_scene_camera
|
599 |
+
|
600 |
+
material_restore.restore()
|
601 |
+
|
602 |
+
if self.is_bipolarization:
|
603 |
+
img_w, img_h, img_c = img.size[0], img.size[1], img.channels
|
604 |
+
pixels = numpy.array(img.pixels).reshape(img_h, img_w, img_c)
|
605 |
+
min = self.bipolarization_threshold - (self.bipolarization_blur / 2.0)
|
606 |
+
max = self.bipolarization_threshold + (self.bipolarization_blur / 2.0)
|
607 |
+
i = numpy.where(pixels[:,:,:3] <= min)
|
608 |
+
pixels[:,:,:3][i] = 0.0
|
609 |
+
i = numpy.where(max <= pixels[:,:,:3])
|
610 |
+
pixels[:,:,:3][i] = 1.0
|
611 |
+
if 0.0 < max - min:
|
612 |
+
i = numpy.where((min < pixels[:,:,:3]) & (pixels[:,:,:3] < max))
|
613 |
+
pixels[:,:,:3][i] -= min
|
614 |
+
pixels[:,:,:3][i] *= 1.0 / (max - min)
|
615 |
+
img.pixels = pixels.flatten()
|
616 |
+
|
617 |
+
return {'FINISHED'}
|
618 |
+
|
619 |
+
class quick_gradation_bake_image(bpy.types.Operator):
|
620 |
+
bl_idname = 'object.quick_gradation_bake_image'
|
621 |
+
bl_label = "Gradient bake"
|
622 |
+
bl_description = "Quickly bakes a gradient to the active object"
|
623 |
+
bl_options = {'REGISTER', 'UNDO'}
|
624 |
+
|
625 |
+
image_name = bpy.props.StringProperty(name="Image Name")
|
626 |
+
items = [
|
627 |
+
('128', "128 px", "", 'LAYER_USED', 1),
|
628 |
+
('256', "256 px", "", 'LAYER_ACTIVE', 2),
|
629 |
+
('512', "512 px", "", 'HAND', 3),
|
630 |
+
('1024', "1024 px", "", 'FILE_TICK', 4),
|
631 |
+
('2048', "2048 px", "", 'ERROR', 5),
|
632 |
+
('4096', "4096 px", "", 'CANCEL', 6),
|
633 |
+
]
|
634 |
+
image_width = bpy.props.EnumProperty(items=items, name="Width", default='1024')
|
635 |
+
image_height = bpy.props.EnumProperty(items=items, name="Height", default='1024')
|
636 |
+
|
637 |
+
@classmethod
|
638 |
+
def poll(cls, context):
|
639 |
+
if len(context.selected_objects) != 1:
|
640 |
+
return False
|
641 |
+
ob = context.active_object
|
642 |
+
if ob:
|
643 |
+
if ob.type == 'MESH':
|
644 |
+
me = ob.data
|
645 |
+
if len(me.uv_layers):
|
646 |
+
return True
|
647 |
+
return False
|
648 |
+
|
649 |
+
def invoke(self, context, event):
|
650 |
+
self.image_name = context.active_object.name + " Gradation Bake"
|
651 |
+
return context.window_manager.invoke_props_dialog(self)
|
652 |
+
|
653 |
+
def draw(self, context):
|
654 |
+
self.layout.label(text="New image settings", icon='IMAGE_COL')
|
655 |
+
self.layout.prop(self, 'image_name', icon='SORTALPHA')
|
656 |
+
row = self.layout.row(align=True)
|
657 |
+
row.prop(self, 'image_width', icon='ARROW_LEFTRIGHT')
|
658 |
+
row.prop(self, 'image_height', icon='NLA_PUSHDOWN')
|
659 |
+
|
660 |
+
def execute(self, context):
|
661 |
+
ob = context.active_object
|
662 |
+
me = ob.data
|
663 |
+
ob.hide_render = False
|
664 |
+
|
665 |
+
override = context.copy()
|
666 |
+
override['object'] = ob
|
667 |
+
|
668 |
+
image_width, image_height = int(self.image_width), int(self.image_height)
|
669 |
+
|
670 |
+
if self.image_name in context.blend_data.images:
|
671 |
+
img = context.blend_data.images[self.image_name]
|
672 |
+
else:
|
673 |
+
img = context.blend_data.images.new(self.image_name, image_width, image_height, alpha=True)
|
674 |
+
|
675 |
+
area = common.get_request_area(context, 'IMAGE_EDITOR')
|
676 |
+
common.set_area_space_attr(area, 'image', img)
|
677 |
+
for elem in me.uv_textures.active.data:
|
678 |
+
elem.image = img
|
679 |
+
|
680 |
+
temp_me = ob.to_mesh(scene=context.scene, apply_modifiers=True, settings='PREVIEW')
|
681 |
+
zs = [(ob.matrix_world * v.co).z for v in temp_me.vertices]
|
682 |
+
zs.sort()
|
683 |
+
me_conter = (zs[0] + zs[-1]) / 2
|
684 |
+
me_height = zs[-1] - zs[0]
|
685 |
+
|
686 |
+
material_restore = common.material_restore(ob)
|
687 |
+
|
688 |
+
bpy.ops.object.material_slot_add(override)
|
689 |
+
temp_mate = context.blend_data.materials.new("quick_gradation_bake_image_temp")
|
690 |
+
ob.material_slots[0].material = temp_mate
|
691 |
+
temp_slot = temp_mate.texture_slots.create(0)
|
692 |
+
temp_tex = context.blend_data.textures.new("quick_gradation_bake_image_temp", 'BLEND')
|
693 |
+
temp_slot.texture = temp_tex
|
694 |
+
temp_tex.use_color_ramp = True
|
695 |
+
temp_slot.mapping_y = 'Z'
|
696 |
+
temp_slot.mapping_z = 'Y'
|
697 |
+
temp_slot.texture_coords = 'GLOBAL'
|
698 |
+
temp_tex.color_ramp.elements[0].color = (0, 0, 0, 1)
|
699 |
+
temp_tex.use_flip_axis = 'VERTICAL'
|
700 |
+
temp_slot.offset[1] = -me_conter
|
701 |
+
temp_slot.scale[1] = 1 / (me_height / 2)
|
702 |
+
|
703 |
+
context.scene.render.bake_type = 'TEXTURE'
|
704 |
+
context.scene.render.use_bake_selected_to_active = False
|
705 |
+
bpy.ops.object.bake_image()
|
706 |
+
|
707 |
+
common.remove_data([temp_me, temp_mate, temp_tex])
|
708 |
+
|
709 |
+
material_restore.restore()
|
710 |
+
|
711 |
+
return {'FINISHED'}
|
712 |
+
|
713 |
+
class quick_metal_bake_image(bpy.types.Operator):
|
714 |
+
bl_idname = 'object.quick_metal_bake_image'
|
715 |
+
bl_label = "Metal Bake"
|
716 |
+
bl_description = "Quickly bake metal object"
|
717 |
+
bl_options = {'REGISTER', 'UNDO'}
|
718 |
+
|
719 |
+
image_name = bpy.props.StringProperty(name="Image Name")
|
720 |
+
items = [
|
721 |
+
('128', "128 px", "", 'LAYER_USED', 1),
|
722 |
+
('256', "256 px", "", 'LAYER_ACTIVE', 2),
|
723 |
+
('512', "512 px", "", 'HAND', 3),
|
724 |
+
('1024', "1024 px", "", 'FILE_TICK', 4),
|
725 |
+
('2048', "2048 px", "", 'ERROR', 5),
|
726 |
+
('4096', "4096 px", "", 'CANCEL', 6),
|
727 |
+
]
|
728 |
+
image_width = bpy.props.EnumProperty(items=items, name="Width", default='1024')
|
729 |
+
image_height = bpy.props.EnumProperty(items=items, name="Height", default='1024')
|
730 |
+
|
731 |
+
mate_color = bpy.props.FloatVectorProperty(name="Color", default=(0.22, 0.22, 0.22), min=0, max=1, soft_min=0, soft_max=1, step=10, precision=2, subtype='COLOR')
|
732 |
+
environment_strength = bpy.props.FloatProperty(name="Strength Reflection", default=1, min=0, max=1, soft_min=0, soft_max=1, step=10, precision=2)
|
733 |
+
highlight_strength = bpy.props.FloatProperty(name="Highlights strength", default=0.5, min=0, max=1, soft_min=0, soft_max=1, step=10, precision=2)
|
734 |
+
|
735 |
+
@classmethod
|
736 |
+
def poll(cls, context):
|
737 |
+
if len(context.selected_objects) != 1:
|
738 |
+
return False
|
739 |
+
ob = context.active_object
|
740 |
+
if ob:
|
741 |
+
if ob.type == 'MESH':
|
742 |
+
me = ob.data
|
743 |
+
if len(me.uv_layers):
|
744 |
+
return True
|
745 |
+
return False
|
746 |
+
|
747 |
+
def invoke(self, context, event):
|
748 |
+
self.image_name = context.active_object.name + " Metal Bake"
|
749 |
+
return context.window_manager.invoke_props_dialog(self)
|
750 |
+
|
751 |
+
def draw(self, context):
|
752 |
+
self.layout.label(text="New image settings", icon='IMAGE_COL')
|
753 |
+
self.layout.prop(self, 'image_name', icon='SORTALPHA')
|
754 |
+
row = self.layout.row(align=True)
|
755 |
+
row.prop(self, 'image_width', icon='ARROW_LEFTRIGHT')
|
756 |
+
row.prop(self, 'image_height', icon='NLA_PUSHDOWN')
|
757 |
+
self.layout.label(text="Metal set", icon='MATCAP_19')
|
758 |
+
self.layout.prop(self, 'mate_color', icon='COLOR')
|
759 |
+
row = self.layout.row(align=True)
|
760 |
+
row.prop(self, 'environment_strength', icon='MATCAP_19', slider=True)
|
761 |
+
row.prop(self, 'highlight_strength', icon='MATCAP_09', slider=True)
|
762 |
+
|
763 |
+
def execute(self, context):
|
764 |
+
ob = context.active_object
|
765 |
+
me = ob.data
|
766 |
+
ob.hide_render = False
|
767 |
+
|
768 |
+
override = context.copy()
|
769 |
+
override['object'] = ob
|
770 |
+
|
771 |
+
image_width, image_height = int(self.image_width), int(self.image_height)
|
772 |
+
|
773 |
+
if self.image_name in context.blend_data.images:
|
774 |
+
img = context.blend_data.images[self.image_name]
|
775 |
+
else:
|
776 |
+
img = context.blend_data.images.new(self.image_name, image_width, image_height, alpha=True)
|
777 |
+
|
778 |
+
area = common.get_request_area(context, 'IMAGE_EDITOR')
|
779 |
+
common.set_area_space_attr(area, 'image', img)
|
780 |
+
for elem in me.uv_textures.active.data:
|
781 |
+
elem.image = img
|
782 |
+
|
783 |
+
hide_render_restore = common.hide_render_restore()
|
784 |
+
material_restore = common.material_restore(ob)
|
785 |
+
|
786 |
+
blend_path = os.path.join(os.path.dirname(__file__), "append_data.blend")
|
787 |
+
with context.blend_data.libraries.load(blend_path) as (data_from, data_to):
|
788 |
+
data_to.materials = ["Metal"]
|
789 |
+
|
790 |
+
bpy.ops.object.material_slot_add(override)
|
791 |
+
temp_mate = data_to.materials[0]
|
792 |
+
ob.material_slots[0].material = temp_mate
|
793 |
+
temp_mate.diffuse_color = self.mate_color[:]
|
794 |
+
temp_mate.texture_slots[0].diffuse_color_factor = self.environment_strength
|
795 |
+
temp_mate.node_tree.nodes["Mix.001"].inputs[0].default_value = 1.0 - self.highlight_strength
|
796 |
+
|
797 |
+
temp_lamp = context.blend_data.lamps.new("quick_metal_bake_image_lamp_temp", 'HEMI')
|
798 |
+
temp_lamp_ob = context.blend_data.objects.new("quick_metal_bake_image_lamp_temp", temp_lamp)
|
799 |
+
context.scene.objects.link(temp_lamp_ob)
|
800 |
+
#temp_lamp.energy = self.lamp_energy
|
801 |
+
|
802 |
+
pre_scene_camera = context.scene.camera
|
803 |
+
temp_camera = context.blend_data.cameras.new("quick_metal_bake_image_camera_temp")
|
804 |
+
temp_camera_ob = context.blend_data.objects.new("quick_metal_bake_image_camera_temp", temp_camera)
|
805 |
+
context.scene.objects.link(temp_camera_ob)
|
806 |
+
temp_camera_ob.rotation_euler[0] = 1.5708
|
807 |
+
context.scene.camera = temp_camera_ob
|
808 |
+
|
809 |
+
context.scene.world.light_settings.use_ambient_occlusion = False
|
810 |
+
|
811 |
+
context.scene.render.bake_type = 'FULL'
|
812 |
+
context.scene.render.use_bake_selected_to_active = False
|
813 |
+
bpy.ops.object.bake_image()
|
814 |
+
|
815 |
+
common.remove_data([temp_mate, temp_lamp_ob, temp_lamp, temp_camera_ob, temp_camera])
|
816 |
+
context.scene.camera = pre_scene_camera
|
817 |
+
|
818 |
+
material_restore.restore()
|
819 |
+
hide_render_restore.restore()
|
820 |
+
|
821 |
+
return {'FINISHED'}
|
822 |
+
|
823 |
+
class quick_hair_bake_image(bpy.types.Operator):
|
824 |
+
bl_idname = 'object.quick_hair_bake_image'
|
825 |
+
bl_label = "Hair"
|
826 |
+
bl_description = "Bake the hairstyle quickly"
|
827 |
+
bl_options = {'REGISTER', 'UNDO'}
|
828 |
+
|
829 |
+
image_name = bpy.props.StringProperty(name="Image Name")
|
830 |
+
items = [
|
831 |
+
('128', "128 px", "", 'LAYER_USED', 1),
|
832 |
+
('256', "256 px", "", 'LAYER_ACTIVE', 2),
|
833 |
+
('512', "512 px", "", 'HAND', 3),
|
834 |
+
('1024', "1024 px", "", 'FILE_TICK', 4),
|
835 |
+
('2048', "2048 px", "", 'ERROR', 5),
|
836 |
+
('4096', "4096 px", "", 'CANCEL', 6),
|
837 |
+
]
|
838 |
+
image_width = bpy.props.EnumProperty(items=items, name="Width", default='1024')
|
839 |
+
image_height = bpy.props.EnumProperty(items=items, name="Height", default='1024')
|
840 |
+
|
841 |
+
mate_diffuse_color = bpy.props.FloatVectorProperty(name="Hair color", default=(1, 1, 1), min=0, max=1, soft_min=0, soft_max=1, step=10, precision=2, subtype='COLOR', size=3)
|
842 |
+
mate_angel_ring_factor = bpy.props.FloatProperty(name="Ring factor", default=0.5, min=0, max=1, soft_min=0, soft_max=1, step=50, precision=2)
|
843 |
+
|
844 |
+
lamp_energy = bpy.props.FloatProperty(name="Light Intensity", default=1, min=0, max=2, soft_min=0, soft_max=2, step=50, precision=2)
|
845 |
+
|
846 |
+
use_ao = bpy.props.BoolProperty(name="Use the AO", default=False)
|
847 |
+
ao_samples = bpy.props.IntProperty(name="Accuracy", default=20, min=1, max=50, soft_min=1, soft_max=50)
|
848 |
+
ao_hide_other = bpy.props.BoolProperty(name="Hide other Objects", default=True)
|
849 |
+
|
850 |
+
@classmethod
|
851 |
+
def poll(cls, context):
|
852 |
+
if len(context.selected_objects) != 1:
|
853 |
+
return False
|
854 |
+
ob = context.active_object
|
855 |
+
if ob:
|
856 |
+
if ob.type == 'MESH':
|
857 |
+
me = ob.data
|
858 |
+
if len(me.uv_layers):
|
859 |
+
return True
|
860 |
+
return False
|
861 |
+
|
862 |
+
def invoke(self, context, event):
|
863 |
+
ob = context.active_object
|
864 |
+
self.image_name = ob.name + " Hair Bake"
|
865 |
+
return context.window_manager.invoke_props_dialog(self)
|
866 |
+
|
867 |
+
def draw(self, context):
|
868 |
+
self.layout.label(text="New image settings", icon='IMAGE_COL')
|
869 |
+
self.layout.prop(self, 'image_name', icon='SORTALPHA')
|
870 |
+
row = self.layout.row(align=True)
|
871 |
+
row.prop(self, 'image_width', icon='ARROW_LEFTRIGHT')
|
872 |
+
row.prop(self, 'image_height', icon='NLA_PUSHDOWN')
|
873 |
+
self.layout.label(text="Hair set", icon='PARTICLEMODE')
|
874 |
+
self.layout.prop(self, 'mate_diffuse_color', icon='COLOR')
|
875 |
+
self.layout.prop(self, 'mate_angel_ring_factor', icon='MATCAP_09', slider=True)
|
876 |
+
self.layout.label(text="Hemi Lamp Settings", icon='LAMP_HEMI')
|
877 |
+
self.layout.prop(self, 'lamp_energy', icon='LAMP_POINT', slider=True)
|
878 |
+
self.layout.label(text="AO Setting", icon='BRUSH_TEXFILL')
|
879 |
+
row = self.layout.row(align=True)
|
880 |
+
row.prop(self, 'use_ao', icon='FILE_TICK')
|
881 |
+
row.prop(self, 'ao_samples', icon='ANIM_DATA')
|
882 |
+
self.layout.prop(self, 'ao_hide_other', icon='VISIBLE_IPO_OFF')
|
883 |
+
|
884 |
+
def execute(self, context):
|
885 |
+
import os.path
|
886 |
+
|
887 |
+
ob = context.active_object
|
888 |
+
me = ob.data
|
889 |
+
ob.hide_render = False
|
890 |
+
|
891 |
+
override = context.copy()
|
892 |
+
override['object'] = ob
|
893 |
+
|
894 |
+
image_width, image_height = int(self.image_width), int(self.image_height)
|
895 |
+
|
896 |
+
if self.image_name in context.blend_data.images:
|
897 |
+
img = context.blend_data.images[self.image_name]
|
898 |
+
else:
|
899 |
+
img = context.blend_data.images.new(self.image_name, image_width, image_height, alpha=True)
|
900 |
+
|
901 |
+
area = common.get_request_area(context, 'IMAGE_EDITOR')
|
902 |
+
common.set_area_space_attr(area, 'image', img)
|
903 |
+
|
904 |
+
for elem in me.uv_textures.active.data:
|
905 |
+
elem.image = img
|
906 |
+
|
907 |
+
if self.ao_hide_other: hide_render_restore = common.hide_render_restore()
|
908 |
+
material_restore = common.material_restore(ob)
|
909 |
+
|
910 |
+
temp_lamp = context.blend_data.lamps.new("quick_hemi_bake_image_lamp_temp", 'HEMI')
|
911 |
+
temp_lamp_ob = context.blend_data.objects.new("quick_hemi_bake_image_lamp_temp", temp_lamp)
|
912 |
+
context.scene.objects.link(temp_lamp_ob)
|
913 |
+
temp_lamp.energy = self.lamp_energy
|
914 |
+
|
915 |
+
pre_scene_camera = context.scene.camera
|
916 |
+
temp_camera = context.blend_data.cameras.new("quick_hemi_bake_image_camera_temp")
|
917 |
+
temp_camera_ob = context.blend_data.objects.new("quick_hemi_bake_image_camera_temp", temp_camera)
|
918 |
+
context.scene.objects.link(temp_camera_ob)
|
919 |
+
temp_camera_ob.rotation_euler[0] = 1.5708
|
920 |
+
context.scene.camera = temp_camera_ob
|
921 |
+
|
922 |
+
|
923 |
+
blend_path = os.path.join(os.path.dirname(__file__), "append_data.blend")
|
924 |
+
with context.blend_data.libraries.load(blend_path) as (data_from, data_to):
|
925 |
+
data_to.materials = ["CM3D2 Hair"]
|
926 |
+
|
927 |
+
bpy.ops.object.material_slot_add(override)
|
928 |
+
temp_mate = data_to.materials[0]
|
929 |
+
ob.material_slots[0].material = temp_mate
|
930 |
+
|
931 |
+
temp_mate.diffuse_color = self.mate_diffuse_color
|
932 |
+
temp_mate.node_tree.nodes["mate_angel_ring_factor"].inputs[0].default_value = self.mate_angel_ring_factor
|
933 |
+
|
934 |
+
context.scene.world.light_settings.use_ambient_occlusion = self.use_ao
|
935 |
+
if self.use_ao:
|
936 |
+
context.scene.world.light_settings.samples = self.ao_samples
|
937 |
+
context.scene.world.light_settings.ao_blend_type = 'MULTIPLY'
|
938 |
+
|
939 |
+
context.scene.render.bake_type = 'FULL'
|
940 |
+
context.scene.render.use_bake_selected_to_active = False
|
941 |
+
bpy.ops.object.bake_image()
|
942 |
+
|
943 |
+
temp_tex = temp_mate.texture_slots[0].texture
|
944 |
+
|
945 |
+
common.remove_data([temp_mate, temp_tex, temp_camera_ob, temp_camera, temp_lamp_ob, temp_lamp])
|
946 |
+
context.scene.camera = pre_scene_camera
|
947 |
+
|
948 |
+
material_restore.restore()
|
949 |
+
if self.ao_hide_other: hide_render_restore.restore()
|
950 |
+
|
951 |
+
return {'FINISHED'}
|
952 |
+
|
953 |
+
class quick_uv_border_bake_image(bpy.types.Operator):
|
954 |
+
bl_idname = 'object.quick_uv_border_bake_image'
|
955 |
+
bl_label = "UV Edge Bake"
|
956 |
+
bl_description = "Quickly bake the edge of the UV"
|
957 |
+
bl_options = {'REGISTER', 'UNDO'}
|
958 |
+
|
959 |
+
image_name = bpy.props.StringProperty(name="Image Name")
|
960 |
+
items = [
|
961 |
+
('128', "128 px", "", 'LAYER_USED', 1),
|
962 |
+
('256', "256 px", "", 'LAYER_ACTIVE', 2),
|
963 |
+
('512', "512 px", "", 'HAND', 3),
|
964 |
+
('1024', "1024 px", "", 'FILE_TICK', 4),
|
965 |
+
('2048', "2048 px", "", 'ERROR', 5),
|
966 |
+
('4096', "4096 px", "", 'CANCEL', 6),
|
967 |
+
]
|
968 |
+
image_width = bpy.props.EnumProperty(items=items, name="Width", default='1024')
|
969 |
+
image_height = bpy.props.EnumProperty(items=items, name="Height", default='1024')
|
970 |
+
|
971 |
+
items = [
|
972 |
+
('FLAT', "Flat", "", 'IPO_CONSTANT', 1),
|
973 |
+
('TENT', "Tent", "", 'IPO_LINEAR', 2),
|
974 |
+
('QUAD', "Quad", "", 'IPO_QUAD', 3),
|
975 |
+
('CUBIC', "Cubic", "", 'IPO_CUBIC', 4),
|
976 |
+
('GAUSS', "Gauss", "", 'HAND', 5),
|
977 |
+
('FAST_GAUSS', "Fast gauss", "", 'ALIASED', 6),
|
978 |
+
('CATROM', "Catrom", "", 'FILE_TICK', 7),
|
979 |
+
('MITCH', "Mitch", "", 'FILE_TICK', 8),
|
980 |
+
]
|
981 |
+
blur_type = bpy.props.EnumProperty(items=items, name="Blur type", default='GAUSS')
|
982 |
+
blur_strength = bpy.props.IntProperty(name="Blur strength", default=100, min=0, max=1000, soft_min=0, soft_max=1000)
|
983 |
+
normalize = bpy.props.BoolProperty(name="Normalize", default=True)
|
984 |
+
keep_alpha = bpy.props.BoolProperty(name="Keep alpha", default=True)
|
985 |
+
|
986 |
+
@classmethod
|
987 |
+
def poll(cls, context):
|
988 |
+
if len(context.selected_objects) != 1:
|
989 |
+
return False
|
990 |
+
ob = context.active_object
|
991 |
+
if ob:
|
992 |
+
if ob.type == 'MESH':
|
993 |
+
me = ob.data
|
994 |
+
if len(me.uv_layers):
|
995 |
+
return True
|
996 |
+
return False
|
997 |
+
|
998 |
+
def invoke(self, context, event):
|
999 |
+
ob = context.active_object
|
1000 |
+
self.image_name = ob.name + " UV Border Bake"
|
1001 |
+
return context.window_manager.invoke_props_dialog(self)
|
1002 |
+
|
1003 |
+
def draw(self, context):
|
1004 |
+
self.layout.label(text="New image settings", icon='IMAGE_COL')
|
1005 |
+
self.layout.prop(self, 'image_name', icon='SORTALPHA')
|
1006 |
+
row = self.layout.row(align=True)
|
1007 |
+
row.prop(self, 'image_width', icon='ARROW_LEFTRIGHT')
|
1008 |
+
row.prop(self, 'image_height', icon='NLA_PUSHDOWN')
|
1009 |
+
self.layout.label(text="Edge Setting", icon='MATCAP_24')
|
1010 |
+
self.layout.prop(self, 'blur_type', icon='BRUSH_BLUR')
|
1011 |
+
self.layout.prop(self, 'blur_strength', icon='ARROW_LEFTRIGHT')
|
1012 |
+
row = self.layout.row(align=True)
|
1013 |
+
row.prop(self, 'normalize', icon='IMAGE_ALPHA')
|
1014 |
+
row.prop(self, 'keep_alpha', icon='IMAGE_RGB_ALPHA')
|
1015 |
+
|
1016 |
+
def execute(self, context):
|
1017 |
+
ob = context.active_object
|
1018 |
+
me = ob.data
|
1019 |
+
ob.hide_render = False
|
1020 |
+
|
1021 |
+
override = context.copy()
|
1022 |
+
override['object'] = ob
|
1023 |
+
|
1024 |
+
image_width, image_height = int(self.image_width), int(self.image_height)
|
1025 |
+
|
1026 |
+
if self.image_name in context.blend_data.images:
|
1027 |
+
img = context.blend_data.images[self.image_name]
|
1028 |
+
else:
|
1029 |
+
img = context.blend_data.images.new(self.image_name, image_width, image_height, alpha=True)
|
1030 |
+
|
1031 |
+
area = common.get_request_area(context, 'IMAGE_EDITOR')
|
1032 |
+
|
1033 |
+
img.generated_color = (0, 0, 0, 1)
|
1034 |
+
|
1035 |
+
for elem in me.uv_textures.active.data:
|
1036 |
+
elem.image = img
|
1037 |
+
|
1038 |
+
material_restore = common.material_restore(ob)
|
1039 |
+
|
1040 |
+
bpy.ops.object.material_slot_add(override)
|
1041 |
+
temp_mate = context.blend_data.materials.new("quick_gradation_bake_image_temp")
|
1042 |
+
ob.material_slots[0].material = temp_mate
|
1043 |
+
temp_mate.diffuse_color = (1, 1, 1)
|
1044 |
+
|
1045 |
+
pre_use_bake_clear = context.scene.render.use_bake_clear
|
1046 |
+
pre_bake_margin = context.scene.render.bake_margin
|
1047 |
+
context.scene.render.use_bake_clear = False
|
1048 |
+
context.scene.render.bake_type = 'TEXTURE'
|
1049 |
+
context.scene.render.use_bake_selected_to_active = False
|
1050 |
+
|
1051 |
+
bpy.ops.object.bake_image()
|
1052 |
+
img_w, img_h, img_c = img.size[0], img.size[1], img.channels
|
1053 |
+
pixels = numpy.array(img.pixels).reshape(img_h, img_w, img_c)
|
1054 |
+
img_alphas = pixels[:,:,0]
|
1055 |
+
|
1056 |
+
img.reload()
|
1057 |
+
context.scene.render.bake_margin = 0
|
1058 |
+
bpy.ops.object.bake_image()
|
1059 |
+
|
1060 |
+
context.scene.render.use_bake_clear = pre_use_bake_clear
|
1061 |
+
context.scene.render.bake_margin = pre_bake_margin
|
1062 |
+
|
1063 |
+
# 無駄に壮大なぼかし処理
|
1064 |
+
pre_resolution_x = context.scene.render.resolution_x
|
1065 |
+
pre_resolution_y = context.scene.render.resolution_y
|
1066 |
+
pre_resolution_percentage = context.scene.render.resolution_percentage
|
1067 |
+
context.scene.render.resolution_x = img.size[0]
|
1068 |
+
context.scene.render.resolution_y = img.size[1]
|
1069 |
+
context.scene.render.resolution_percentage = 100
|
1070 |
+
|
1071 |
+
context.scene.use_nodes = True
|
1072 |
+
node_tree = context.scene.node_tree
|
1073 |
+
for node in node_tree.nodes:
|
1074 |
+
node_tree.nodes.remove(node)
|
1075 |
+
|
1076 |
+
img_node = node_tree.nodes.new('CompositorNodeImage')
|
1077 |
+
img_node.location = (0, 0)
|
1078 |
+
img_node.image = img
|
1079 |
+
|
1080 |
+
blur_node = node_tree.nodes.new('CompositorNodeBlur')
|
1081 |
+
blur_node.location = (250, 0)
|
1082 |
+
blur_node.size_x, blur_node.size_y = 1, 1
|
1083 |
+
blur_node.filter_type = self.blur_type
|
1084 |
+
blur_node.inputs[1].default_value = self.blur_strength
|
1085 |
+
|
1086 |
+
out_node = node_tree.nodes.new('CompositorNodeComposite')
|
1087 |
+
out_node.location = (500, 0)
|
1088 |
+
|
1089 |
+
node_tree.links.new(blur_node.inputs[0], img_node.outputs[0])
|
1090 |
+
node_tree.links.new(out_node.inputs[0], blur_node.outputs[0])
|
1091 |
+
|
1092 |
+
bpy.ops.render.render()
|
1093 |
+
|
1094 |
+
render_img = context.blend_data.images["Render Result"]
|
1095 |
+
|
1096 |
+
temp_png_path = os.path.join(bpy.app.tempdir, "temp.png")
|
1097 |
+
img_override = context.copy()
|
1098 |
+
img_override['object'] = render_img
|
1099 |
+
img_override['edit_image'] = render_img
|
1100 |
+
img_override['area'] = area
|
1101 |
+
common.set_area_space_attr(area, 'image', render_img)
|
1102 |
+
bpy.ops.image.save_as(img_override, save_as_render=True, copy=True, filepath=temp_png_path, relative_path=False, show_multiview=False, use_multiview=False)
|
1103 |
+
img.source = 'FILE'
|
1104 |
+
img.filepath = temp_png_path
|
1105 |
+
img.reload()
|
1106 |
+
pixels = numpy.array(img.pixels).reshape(img_h, img_w, img_c)
|
1107 |
+
if self.keep_alpha:
|
1108 |
+
pixels[:,:,3] = img_alphas
|
1109 |
+
if self.normalize:
|
1110 |
+
pixels[:,:,:3] -= 0.5
|
1111 |
+
pixels[:,:,:3] *= 2.0
|
1112 |
+
img.pixels = pixels.flatten()
|
1113 |
+
img.pack(as_png=True)
|
1114 |
+
os.remove(temp_png_path)
|
1115 |
+
|
1116 |
+
for node in node_tree.nodes:
|
1117 |
+
node_tree.nodes.remove(node)
|
1118 |
+
context.scene.use_nodes = False
|
1119 |
+
context.scene.render.resolution_x = pre_resolution_x
|
1120 |
+
context.scene.render.resolution_y = pre_resolution_y
|
1121 |
+
context.scene.render.resolution_percentage = pre_resolution_percentage
|
1122 |
+
# 無駄に壮大なぼかし処理 完
|
1123 |
+
|
1124 |
+
common.set_area_space_attr(area, 'image', img)
|
1125 |
+
common.remove_data([temp_mate])
|
1126 |
+
material_restore.restore()
|
1127 |
+
|
1128 |
+
return {'FINISHED'}
|
1129 |
+
|
1130 |
+
class quick_mesh_border_bake_image(bpy.types.Operator):
|
1131 |
+
bl_idname = 'object.quick_mesh_border_bake_image'
|
1132 |
+
bl_label = "Mesh edge bake"
|
1133 |
+
bl_description = "bake the edge of the mesh to the object"
|
1134 |
+
bl_options = {'REGISTER', 'UNDO'}
|
1135 |
+
|
1136 |
+
image_name = bpy.props.StringProperty(name="Image Name")
|
1137 |
+
items = [
|
1138 |
+
('128', "128 px", "", 'LAYER_USED', 1),
|
1139 |
+
('256', "256 px", "", 'LAYER_ACTIVE', 2),
|
1140 |
+
('512', "512 px", "", 'HAND', 3),
|
1141 |
+
('1024', "1024 px", "", 'FILE_TICK', 4),
|
1142 |
+
('2048', "2048 px", "", 'ERROR', 5),
|
1143 |
+
('4096', "4096 px", "", 'CANCEL', 6),
|
1144 |
+
]
|
1145 |
+
image_width = bpy.props.EnumProperty(items=items, name="Width", default='1024')
|
1146 |
+
image_height = bpy.props.EnumProperty(items=items, name="Height", default='1024')
|
1147 |
+
|
1148 |
+
range = bpy.props.IntProperty(name="Range", default=5, min=1, max=50, soft_min=1, soft_max=50)
|
1149 |
+
|
1150 |
+
@classmethod
|
1151 |
+
def poll(cls, context):
|
1152 |
+
if len(context.selected_objects) != 1:
|
1153 |
+
return False
|
1154 |
+
ob = context.active_object
|
1155 |
+
if ob:
|
1156 |
+
if ob.type == 'MESH':
|
1157 |
+
me = ob.data
|
1158 |
+
if len(me.uv_layers):
|
1159 |
+
return True
|
1160 |
+
return False
|
1161 |
+
|
1162 |
+
def invoke(self, context, event):
|
1163 |
+
ob = context.active_object
|
1164 |
+
self.image_name = ob.name + " Mesh Border Bake"
|
1165 |
+
return context.window_manager.invoke_props_dialog(self)
|
1166 |
+
|
1167 |
+
def draw(self, context):
|
1168 |
+
self.layout.label(text="New image settings", icon='IMAGE_COL')
|
1169 |
+
self.layout.prop(self, 'image_name', icon='SORTALPHA')
|
1170 |
+
row = self.layout.row(align=True)
|
1171 |
+
row.prop(self, 'image_width', icon='ARROW_LEFTRIGHT')
|
1172 |
+
row.prop(self, 'image_height', icon='NLA_PUSHDOWN')
|
1173 |
+
|
1174 |
+
self.layout.prop(self, 'range', icon='PROP_ON')
|
1175 |
+
|
1176 |
+
def execute(self, context):
|
1177 |
+
ob = context.active_object
|
1178 |
+
me = ob.data
|
1179 |
+
ob.select = False
|
1180 |
+
ob.hide_render = False
|
1181 |
+
|
1182 |
+
image_width, image_height = int(self.image_width), int(self.image_height)
|
1183 |
+
|
1184 |
+
if self.image_name in context.blend_data.images:
|
1185 |
+
img = context.blend_data.images[self.image_name]
|
1186 |
+
else:
|
1187 |
+
img = context.blend_data.images.new(self.image_name, image_width, image_height, alpha=True)
|
1188 |
+
|
1189 |
+
area = common.get_request_area(context, 'IMAGE_EDITOR')
|
1190 |
+
common.set_area_space_attr(area, 'image', img)
|
1191 |
+
for elem in me.uv_textures.active.data:
|
1192 |
+
elem.image = img
|
1193 |
+
|
1194 |
+
temp_me = ob.to_mesh(scene=context.scene, apply_modifiers=True, settings='PREVIEW')
|
1195 |
+
temp_ob = context.blend_data.objects.new("quick_density_bake_image", temp_me)
|
1196 |
+
context.scene.objects.link(temp_ob)
|
1197 |
+
for vc in temp_me.vertex_colors:
|
1198 |
+
temp_me.vertex_colors.remove(vc)
|
1199 |
+
temp_vertex_color = temp_me.vertex_colors.new(name="quick_density_bake_image")
|
1200 |
+
context.scene.objects.active = temp_ob
|
1201 |
+
temp_ob.select = True
|
1202 |
+
|
1203 |
+
def paint_selected_vertices(me, color, except_indices=[]):
|
1204 |
+
paint_vertices = []
|
1205 |
+
for vert in me.vertices:
|
1206 |
+
if vert.select and vert.index not in except_indices:
|
1207 |
+
paint_vertices.append(vert.index)
|
1208 |
+
for loop in me.loops:
|
1209 |
+
if loop.vertex_index in paint_vertices:
|
1210 |
+
me.vertex_colors.active.data[loop.index].color = color
|
1211 |
+
return paint_vertices
|
1212 |
+
|
1213 |
+
context.tool_settings.mesh_select_mode = (True, False, False)
|
1214 |
+
already_vert_indices = []
|
1215 |
+
for index in range(self.range):
|
1216 |
+
bpy.ops.object.mode_set(mode='EDIT')
|
1217 |
+
if index == 0:
|
1218 |
+
bpy.ops.mesh.reveal()
|
1219 |
+
bpy.ops.mesh.select_all(action='DESELECT')
|
1220 |
+
bpy.ops.mesh.select_non_manifold()
|
1221 |
+
else:
|
1222 |
+
bpy.ops.mesh.select_more()
|
1223 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
1224 |
+
|
1225 |
+
value = (1.0 / self.range) * index
|
1226 |
+
already_vert_indices += paint_selected_vertices(temp_me, [value, value, value], already_vert_indices)
|
1227 |
+
|
1228 |
+
bpy.ops.object.mode_set(mode='EDIT')
|
1229 |
+
bpy.ops.mesh.select_all(action='DESELECT')
|
1230 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
1231 |
+
|
1232 |
+
context.scene.render.bake_type = 'VERTEX_COLORS'
|
1233 |
+
context.scene.render.use_bake_selected_to_active = False
|
1234 |
+
bpy.ops.object.bake_image()
|
1235 |
+
|
1236 |
+
common.remove_data([temp_me, temp_ob])
|
1237 |
+
context.scene.objects.active = ob
|
1238 |
+
ob.select = True
|
1239 |
+
|
1240 |
+
return {'FINISHED'}
|
1241 |
+
|
1242 |
+
class quick_density_bake_image(bpy.types.Operator):
|
1243 |
+
bl_idname = 'object.quick_density_bake_image'
|
1244 |
+
bl_label = "Density bake"
|
1245 |
+
bl_description = "Bakes density in to the active object"
|
1246 |
+
bl_options = {'REGISTER', 'UNDO'}
|
1247 |
+
|
1248 |
+
image_name = bpy.props.StringProperty(name="Image Name")
|
1249 |
+
items = [
|
1250 |
+
('128', "128 px", "", 'LAYER_USED', 1),
|
1251 |
+
('256', "256 px", "", 'LAYER_ACTIVE', 2),
|
1252 |
+
('512', "512 px", "", 'HAND', 3),
|
1253 |
+
('1024', "1024 px", "", 'FILE_TICK', 4),
|
1254 |
+
('2048', "2048 px", "", 'ERROR', 5),
|
1255 |
+
('4096', "4096 px", "", 'CANCEL', 6),
|
1256 |
+
]
|
1257 |
+
image_width = bpy.props.EnumProperty(items=items, name="Width", default='1024')
|
1258 |
+
image_height = bpy.props.EnumProperty(items=items, name="Height", default='1024')
|
1259 |
+
|
1260 |
+
items = [
|
1261 |
+
('ALL', "All", "", 'MOD_SUBSURF', 1),
|
1262 |
+
('PARTS', "Each Part", "", 'GROUP_VCOL', 2),
|
1263 |
+
]
|
1264 |
+
mode = bpy.props.EnumProperty(items=items, name="Comparison", default='PARTS')
|
1265 |
+
|
1266 |
+
@classmethod
|
1267 |
+
def poll(cls, context):
|
1268 |
+
if len(context.selected_objects) != 1:
|
1269 |
+
return False
|
1270 |
+
ob = context.active_object
|
1271 |
+
if ob:
|
1272 |
+
if ob.type == 'MESH':
|
1273 |
+
me = ob.data
|
1274 |
+
if len(me.uv_layers):
|
1275 |
+
return True
|
1276 |
+
return False
|
1277 |
+
|
1278 |
+
def invoke(self, context, event):
|
1279 |
+
ob = context.active_object
|
1280 |
+
self.image_name = ob.name + " Density Bake"
|
1281 |
+
return context.window_manager.invoke_props_dialog(self)
|
1282 |
+
|
1283 |
+
def draw(self, context):
|
1284 |
+
self.layout.label(text="New image settings", icon='IMAGE_COL')
|
1285 |
+
self.layout.prop(self, 'image_name', icon='SORTALPHA')
|
1286 |
+
row = self.layout.row(align=True)
|
1287 |
+
row.prop(self, 'image_width', icon='ARROW_LEFTRIGHT')
|
1288 |
+
row.prop(self, 'image_height', icon='NLA_PUSHDOWN')
|
1289 |
+
self.layout.label(text="Comparision", icon='ZOOM_PREVIOUS')
|
1290 |
+
self.layout.prop(self, 'mode', icon='ZOOM_PREVIOUS', expand=True)
|
1291 |
+
|
1292 |
+
def execute(self, context):
|
1293 |
+
ob = context.active_object
|
1294 |
+
me = ob.data
|
1295 |
+
ob.select = False
|
1296 |
+
ob.hide_render = False
|
1297 |
+
|
1298 |
+
image_width, image_height = int(self.image_width), int(self.image_height)
|
1299 |
+
|
1300 |
+
if self.image_name in context.blend_data.images:
|
1301 |
+
img = context.blend_data.images[self.image_name]
|
1302 |
+
else:
|
1303 |
+
img = context.blend_data.images.new(self.image_name, image_width, image_height, alpha=True)
|
1304 |
+
|
1305 |
+
area = common.get_request_area(context, 'IMAGE_EDITOR')
|
1306 |
+
common.set_area_space_attr(area, 'image', img)
|
1307 |
+
for elem in me.uv_textures.active.data:
|
1308 |
+
elem.image = img
|
1309 |
+
|
1310 |
+
temp_me = ob.to_mesh(scene=context.scene, apply_modifiers=True, settings='PREVIEW')
|
1311 |
+
temp_ob = context.blend_data.objects.new("quick_density_bake_image", temp_me)
|
1312 |
+
context.scene.objects.link(temp_ob)
|
1313 |
+
for vc in temp_me.vertex_colors:
|
1314 |
+
temp_me.vertex_colors.remove(vc)
|
1315 |
+
temp_vertex_color = temp_me.vertex_colors.new(name="quick_density_bake_image")
|
1316 |
+
context.scene.objects.active = temp_ob
|
1317 |
+
temp_ob.select = True
|
1318 |
+
|
1319 |
+
bm = bmesh.new()
|
1320 |
+
bm.from_mesh(temp_me)
|
1321 |
+
bm.verts.ensure_lookup_table()
|
1322 |
+
|
1323 |
+
vert_islands = []
|
1324 |
+
if self.mode == 'ALL':
|
1325 |
+
vert_islands.append([v.index for v in bm.verts])
|
1326 |
+
elif self.mode == 'PARTS':
|
1327 |
+
alread_vert_indices = []
|
1328 |
+
for i in range(9**9):
|
1329 |
+
|
1330 |
+
vert_islands.append([])
|
1331 |
+
|
1332 |
+
for vert in bm.verts:
|
1333 |
+
if vert.index not in alread_vert_indices:
|
1334 |
+
new_verts = [vert]
|
1335 |
+
alread_vert_indices.append(vert.index)
|
1336 |
+
vert_islands[-1].append(vert.index)
|
1337 |
+
break
|
1338 |
+
|
1339 |
+
for j in range(9**9):
|
1340 |
+
|
1341 |
+
vs = []
|
1342 |
+
for vert in new_verts:
|
1343 |
+
for edge in vert.link_edges:
|
1344 |
+
for v in edge.verts:
|
1345 |
+
if vert.index != v.index and v.index not in alread_vert_indices:
|
1346 |
+
vs.append(v)
|
1347 |
+
alread_vert_indices.append(v.index)
|
1348 |
+
vert_islands[-1].append(v.index)
|
1349 |
+
break
|
1350 |
+
|
1351 |
+
if not len(vs):
|
1352 |
+
break
|
1353 |
+
|
1354 |
+
new_verts = vs[:]
|
1355 |
+
|
1356 |
+
if len(bm.verts) <= len(alread_vert_indices):
|
1357 |
+
break
|
1358 |
+
|
1359 |
+
for island in vert_islands:
|
1360 |
+
edge_lens = []
|
1361 |
+
for index in island:
|
1362 |
+
lens = [e.calc_length() for e in bm.verts[index].link_edges]
|
1363 |
+
edge_lens.append( sum(lens) / len(lens) )
|
1364 |
+
edge_min, edge_max = min(edge_lens), max(edge_lens)
|
1365 |
+
try:
|
1366 |
+
multi = 1.0 / (edge_max - edge_min)
|
1367 |
+
except:
|
1368 |
+
multi = 1.0
|
1369 |
+
|
1370 |
+
for index in island:
|
1371 |
+
vert = bm.verts[index]
|
1372 |
+
|
1373 |
+
lens = [e.calc_length() for e in vert.link_edges]
|
1374 |
+
l = sum(lens) / len(lens)
|
1375 |
+
value = (l - edge_min) * multi
|
1376 |
+
for loop in vert.link_loops:
|
1377 |
+
temp_vertex_color.data[loop.index].color = (value, value, value)
|
1378 |
+
|
1379 |
+
context.scene.render.bake_type = 'VERTEX_COLORS'
|
1380 |
+
context.scene.render.use_bake_selected_to_active = False
|
1381 |
+
bpy.ops.object.bake_image()
|
1382 |
+
|
1383 |
+
common.remove_data([temp_me, temp_ob])
|
1384 |
+
context.scene.objects.active = ob
|
1385 |
+
ob.select = True
|
1386 |
+
|
1387 |
+
return {'FINISHED'}
|
1388 |
+
|
1389 |
+
class quick_mesh_distance_bake_image(bpy.types.Operator):
|
1390 |
+
bl_idname = 'object.quick_mesh_distance_bake_image'
|
1391 |
+
bl_label = "Mesh distance bake"
|
1392 |
+
bl_description = "Bake the distance between the other objects in the active object"
|
1393 |
+
bl_options = {'REGISTER', 'UNDO'}
|
1394 |
+
|
1395 |
+
image_name = bpy.props.StringProperty(name="Image Name")
|
1396 |
+
items = [
|
1397 |
+
('128', "128 px", "", 'LAYER_USED', 1),
|
1398 |
+
('256', "256 px", "", 'LAYER_ACTIVE', 2),
|
1399 |
+
('512', "512 px", "", 'HAND', 3),
|
1400 |
+
('1024', "1024 px", "", 'FILE_TICK', 4),
|
1401 |
+
('2048', "2048 px", "", 'ERROR', 5),
|
1402 |
+
('4096', "4096 px", "", 'CANCEL', 6),
|
1403 |
+
]
|
1404 |
+
image_width = bpy.props.EnumProperty(items=items, name="Width", default='1024')
|
1405 |
+
image_height = bpy.props.EnumProperty(items=items, name="Height", default='1024')
|
1406 |
+
|
1407 |
+
@classmethod
|
1408 |
+
def poll(cls, context):
|
1409 |
+
obs = context.selected_objects
|
1410 |
+
if len(obs) != 2: return False
|
1411 |
+
for ob in obs:
|
1412 |
+
if ob.type != 'MESH':
|
1413 |
+
return False
|
1414 |
+
me = context.active_object.data
|
1415 |
+
if len(me.uv_layers):
|
1416 |
+
return True
|
1417 |
+
return False
|
1418 |
+
|
1419 |
+
def invoke(self, context, event):
|
1420 |
+
ob = context.active_object
|
1421 |
+
self.image_name = ob.name + " Mesh Distance Bake"
|
1422 |
+
return context.window_manager.invoke_props_dialog(self)
|
1423 |
+
|
1424 |
+
def draw(self, context):
|
1425 |
+
self.layout.label(text="New image settings", icon='IMAGE_COL')
|
1426 |
+
self.layout.prop(self, 'image_name', icon='SORTALPHA')
|
1427 |
+
row = self.layout.row(align=True)
|
1428 |
+
row.prop(self, 'image_width', icon='ARROW_LEFTRIGHT')
|
1429 |
+
row.prop(self, 'image_height', icon='NLA_PUSHDOWN')
|
1430 |
+
|
1431 |
+
def execute(self, context):
|
1432 |
+
target_ob = context.active_object
|
1433 |
+
target_ob.hide_render = False
|
1434 |
+
for ob in context.selected_objects:
|
1435 |
+
if ob.name != target_ob.name:
|
1436 |
+
source_ob = ob
|
1437 |
+
ob.select = False
|
1438 |
+
target_me = target_ob.data
|
1439 |
+
source_me = source_ob.data
|
1440 |
+
|
1441 |
+
image_width, image_height = int(self.image_width), int(self.image_height)
|
1442 |
+
|
1443 |
+
if self.image_name in context.blend_data.images:
|
1444 |
+
img = context.blend_data.images[self.image_name]
|
1445 |
+
else:
|
1446 |
+
img = context.blend_data.images.new(self.image_name, image_width, image_height, alpha=True)
|
1447 |
+
|
1448 |
+
area = common.get_request_area(context, 'IMAGE_EDITOR')
|
1449 |
+
common.set_area_space_attr(area, 'image', img)
|
1450 |
+
for elem in target_me.uv_textures.active.data:
|
1451 |
+
elem.image = img
|
1452 |
+
|
1453 |
+
temp_me = target_ob.to_mesh(scene=context.scene, apply_modifiers=True, settings='PREVIEW')
|
1454 |
+
temp_ob = context.blend_data.objects.new("quick_density_bake_image", temp_me)
|
1455 |
+
context.scene.objects.link(temp_ob)
|
1456 |
+
for vc in temp_me.vertex_colors:
|
1457 |
+
temp_me.vertex_colors.remove(vc)
|
1458 |
+
temp_vertex_color = temp_me.vertex_colors.new(name="quick_density_bake_image")
|
1459 |
+
context.scene.objects.active = temp_ob
|
1460 |
+
temp_ob.select = True
|
1461 |
+
|
1462 |
+
bvh = mathutils.bvhtree.BVHTree.FromObject(source_ob, context.scene)
|
1463 |
+
|
1464 |
+
vert_dists = []
|
1465 |
+
for vert in temp_me.vertices:
|
1466 |
+
co = target_ob.matrix_world * vert.co
|
1467 |
+
location, normal, index, dist = bvh.find(co)
|
1468 |
+
vert_dists.append(dist)
|
1469 |
+
|
1470 |
+
dist_min, dist_max = min(vert_dists), max(vert_dists)
|
1471 |
+
try:
|
1472 |
+
multi = 1.0 / (dist_max - dist_min)
|
1473 |
+
except:
|
1474 |
+
multi = 1.0
|
1475 |
+
|
1476 |
+
for loop in temp_me.loops:
|
1477 |
+
value = ( vert_dists[loop.vertex_index] - dist_min ) * multi
|
1478 |
+
temp_vertex_color.data[loop.index].color = (value, value, value)
|
1479 |
+
|
1480 |
+
context.scene.render.bake_type = 'VERTEX_COLORS'
|
1481 |
+
context.scene.render.use_bake_selected_to_active = False
|
1482 |
+
bpy.ops.object.bake_image()
|
1483 |
+
|
1484 |
+
common.remove_data([temp_me, temp_ob])
|
1485 |
+
context.scene.objects.active = target_ob
|
1486 |
+
target_ob.select = True
|
1487 |
+
source_ob.select = True
|
1488 |
+
|
1489 |
+
return {'FINISHED'}
|
1490 |
+
|
1491 |
+
class quick_bulge_bake_image(bpy.types.Operator):
|
1492 |
+
bl_idname = 'object.quick_bulge_bake_image'
|
1493 |
+
bl_label = "Bulge Bake"
|
1494 |
+
bl_description = "Quick bakes the parts that bulge"
|
1495 |
+
bl_options = {'REGISTER', 'UNDO'}
|
1496 |
+
|
1497 |
+
image_name = bpy.props.StringProperty(name="Image Name")
|
1498 |
+
items = [
|
1499 |
+
('128', "128 px", "", 'LAYER_USED', 1),
|
1500 |
+
('256', "256 px", "", 'LAYER_ACTIVE', 2),
|
1501 |
+
('512', "512 px", "", 'HAND', 3),
|
1502 |
+
('1024', "1024 px", "", 'FILE_TICK', 4),
|
1503 |
+
('2048', "2048 px", "", 'ERROR', 5),
|
1504 |
+
('4096', "4096 px", "", 'CANCEL', 6),
|
1505 |
+
]
|
1506 |
+
image_width = bpy.props.EnumProperty(items=items, name="Width", default='1024')
|
1507 |
+
image_height = bpy.props.EnumProperty(items=items, name="Height", default='1024')
|
1508 |
+
|
1509 |
+
@classmethod
|
1510 |
+
def poll(cls, context):
|
1511 |
+
if len(context.selected_objects) != 1:
|
1512 |
+
return False
|
1513 |
+
ob = context.active_object
|
1514 |
+
if ob:
|
1515 |
+
if ob.type == 'MESH':
|
1516 |
+
me = ob.data
|
1517 |
+
if len(me.uv_layers):
|
1518 |
+
return True
|
1519 |
+
return False
|
1520 |
+
|
1521 |
+
def invoke(self, context, event):
|
1522 |
+
ob = context.active_object
|
1523 |
+
self.image_name = ob.name + " Bulge Bake"
|
1524 |
+
return context.window_manager.invoke_props_dialog(self)
|
1525 |
+
|
1526 |
+
def draw(self, context):
|
1527 |
+
self.layout.label(text="New image settings", icon='IMAGE_COL')
|
1528 |
+
self.layout.prop(self, 'image_name', icon='SORTALPHA')
|
1529 |
+
row = self.layout.row(align=True)
|
1530 |
+
row.prop(self, 'image_width', icon='ARROW_LEFTRIGHT')
|
1531 |
+
row.prop(self, 'image_height', icon='NLA_PUSHDOWN')
|
1532 |
+
|
1533 |
+
def execute(self, context):
|
1534 |
+
ob = context.active_object
|
1535 |
+
me = ob.data
|
1536 |
+
ob.select = False
|
1537 |
+
ob.hide_render = False
|
1538 |
+
|
1539 |
+
image_width, image_height = int(self.image_width), int(self.image_height)
|
1540 |
+
|
1541 |
+
if self.image_name in context.blend_data.images:
|
1542 |
+
img = context.blend_data.images[self.image_name]
|
1543 |
+
else:
|
1544 |
+
img = context.blend_data.images.new(self.image_name, image_width, image_height, alpha=True)
|
1545 |
+
|
1546 |
+
area = common.get_request_area(context, 'IMAGE_EDITOR')
|
1547 |
+
common.set_area_space_attr(area, 'image', img)
|
1548 |
+
for elem in me.uv_textures.active.data:
|
1549 |
+
elem.image = img
|
1550 |
+
|
1551 |
+
temp_me = ob.to_mesh(scene=context.scene, apply_modifiers=True, settings='PREVIEW')
|
1552 |
+
temp_ob = context.blend_data.objects.new("quick_bulge_bake_image", temp_me)
|
1553 |
+
context.scene.objects.link(temp_ob)
|
1554 |
+
for vc in temp_me.vertex_colors:
|
1555 |
+
temp_me.vertex_colors.remove(vc)
|
1556 |
+
temp_vertex_color = temp_me.vertex_colors.new(name="quick_bulge_bake_image")
|
1557 |
+
context.scene.objects.active = temp_ob
|
1558 |
+
temp_ob.select = True
|
1559 |
+
|
1560 |
+
bm = bmesh.new()
|
1561 |
+
bm.from_mesh(temp_me)
|
1562 |
+
|
1563 |
+
angles = []
|
1564 |
+
for vert in bm.verts:
|
1565 |
+
normal = vert.normal
|
1566 |
+
edge_angle_total = 0.0
|
1567 |
+
for edge in vert.link_edges:
|
1568 |
+
diff_co = edge.other_vert(vert).co - vert.co
|
1569 |
+
if 0 < diff_co.length:
|
1570 |
+
edge_angle_total += normal.angle(diff_co)
|
1571 |
+
if len(vert.link_edges):
|
1572 |
+
edge_angle = edge_angle_total / len(vert.link_edges)
|
1573 |
+
else:
|
1574 |
+
edge_angle = 0.0
|
1575 |
+
angles.append(edge_angle)
|
1576 |
+
|
1577 |
+
angle_min, angle_max = 1.5708, max(angles)
|
1578 |
+
multi = 1.0 / (angle_max - angle_min)
|
1579 |
+
|
1580 |
+
for vert in bm.verts:
|
1581 |
+
value = (angles[vert.index] - angle_min) * multi
|
1582 |
+
for loop in vert.link_loops:
|
1583 |
+
temp_vertex_color.data[loop.index].color = (value, value, value)
|
1584 |
+
|
1585 |
+
context.scene.render.bake_type = 'VERTEX_COLORS'
|
1586 |
+
context.scene.render.use_bake_selected_to_active = False
|
1587 |
+
bpy.ops.object.bake_image()
|
1588 |
+
|
1589 |
+
common.remove_data([temp_me, temp_ob])
|
1590 |
+
context.scene.objects.active = ob
|
1591 |
+
ob.select = True
|
1592 |
+
|
1593 |
+
return {'FINISHED'}
|
1594 |
+
|
1595 |
+
class quick_semen_bake_image(bpy.types.Operator):
|
1596 |
+
bl_idname = 'object.quick_semen_bake_image'
|
1597 |
+
bl_label = "White liquid bake AKA Semen"
|
1598 |
+
bl_description = "Bake the white liquid to the object..."
|
1599 |
+
bl_options = {'REGISTER', 'UNDO'}
|
1600 |
+
|
1601 |
+
image_name = bpy.props.StringProperty(name="Image Name")
|
1602 |
+
items = [
|
1603 |
+
('128', "128 px", "", 'LAYER_USED', 1),
|
1604 |
+
('256', "256 px", "", 'LAYER_ACTIVE', 2),
|
1605 |
+
('512', "512 px", "", 'HAND', 3),
|
1606 |
+
('1024', "1024 px", "", 'FILE_TICK', 4),
|
1607 |
+
('2048', "2048 px", "", 'ERROR', 5),
|
1608 |
+
('4096', "4096 px", "", 'CANCEL', 6),
|
1609 |
+
]
|
1610 |
+
image_width = bpy.props.EnumProperty(items=items, name="Width", default='1024')
|
1611 |
+
image_height = bpy.props.EnumProperty(items=items, name="Height", default='1024')
|
1612 |
+
|
1613 |
+
texture_scale = bpy.props.FloatProperty(name="Texture size", default=1, min=0, max=100, soft_min=0, soft_max=100, step=50, precision=1)
|
1614 |
+
|
1615 |
+
@classmethod
|
1616 |
+
def poll(cls, context):
|
1617 |
+
if len(context.selected_objects) != 1:
|
1618 |
+
return False
|
1619 |
+
ob = context.active_object
|
1620 |
+
if ob:
|
1621 |
+
if ob.type == 'MESH':
|
1622 |
+
me = ob.data
|
1623 |
+
if len(me.uv_layers):
|
1624 |
+
return True
|
1625 |
+
return False
|
1626 |
+
|
1627 |
+
def invoke(self, context, event):
|
1628 |
+
ob = context.active_object
|
1629 |
+
self.image_name = ob.name + " Semen Bake"
|
1630 |
+
return context.window_manager.invoke_props_dialog(self)
|
1631 |
+
|
1632 |
+
def draw(self, context):
|
1633 |
+
self.layout.label(text="New image settings", icon='IMAGE_COL')
|
1634 |
+
self.layout.prop(self, 'image_name', icon='SORTALPHA')
|
1635 |
+
row = self.layout.row(align=True)
|
1636 |
+
row.prop(self, 'image_width', icon='ARROW_LEFTRIGHT')
|
1637 |
+
row.prop(self, 'image_height', icon='NLA_PUSHDOWN')
|
1638 |
+
|
1639 |
+
self.layout.prop(self, 'texture_scale', icon='TEXTURE')
|
1640 |
+
|
1641 |
+
def execute(self, context):
|
1642 |
+
ob = context.active_object
|
1643 |
+
me = ob.data
|
1644 |
+
ob.hide_render = False
|
1645 |
+
|
1646 |
+
override = context.copy()
|
1647 |
+
override['object'] = ob
|
1648 |
+
|
1649 |
+
image_width, image_height = int(self.image_width), int(self.image_height)
|
1650 |
+
|
1651 |
+
if self.image_name in context.blend_data.images:
|
1652 |
+
img = context.blend_data.images[self.image_name]
|
1653 |
+
else:
|
1654 |
+
img = context.blend_data.images.new(self.image_name, image_width, image_height, alpha=True)
|
1655 |
+
|
1656 |
+
area = common.get_request_area(context, 'IMAGE_EDITOR')
|
1657 |
+
common.set_area_space_attr(area, 'image', img)
|
1658 |
+
for elem in me.uv_textures.active.data:
|
1659 |
+
elem.image = img
|
1660 |
+
|
1661 |
+
material_restore = common.material_restore(ob)
|
1662 |
+
|
1663 |
+
blend_path = os.path.join(os.path.dirname(__file__), "append_data.blend")
|
1664 |
+
with context.blend_data.libraries.load(blend_path) as (data_from, data_to):
|
1665 |
+
data_to.materials = ["Semen"]
|
1666 |
+
|
1667 |
+
bpy.ops.object.material_slot_add(override)
|
1668 |
+
temp_mate = data_to.materials[0]
|
1669 |
+
ob.material_slots[0].material = temp_mate
|
1670 |
+
temp_mate.texture_slots[0].scale = (self.texture_scale, self.texture_scale, self.texture_scale)
|
1671 |
+
|
1672 |
+
context.scene.render.bake_type = 'TEXTURE'
|
1673 |
+
context.scene.render.use_bake_selected_to_active = False
|
1674 |
+
bpy.ops.object.bake_image()
|
1675 |
+
|
1676 |
+
common.remove_data([temp_mate, temp_mate.texture_slots[0].texture, temp_mate.texture_slots[0].texture.image])
|
1677 |
+
|
1678 |
+
material_restore.restore()
|
1679 |
+
|
1680 |
+
return {'FINISHED'}
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_RENDER_PT_render.py
ADDED
@@ -0,0 +1,306 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 「プロパティ」エリア → 「レンダー」タブ → 「レンダー」パネル
|
2 |
+
import os, re, sys, bpy, time, bmesh, mathutils
|
3 |
+
from . import common
|
4 |
+
|
5 |
+
# メニュー等に項目追加
|
6 |
+
def menu_func(self, context):
|
7 |
+
self.layout.operator('render.render_cm3d2_icon', icon_value=common.preview_collections['main']['KISS'].icon_id)
|
8 |
+
|
9 |
+
class render_cm3d2_icon(bpy.types.Operator):
|
10 |
+
bl_idname = 'render.render_cm3d2_icon'
|
11 |
+
bl_label = "Render a CM3D2 Style Icon"
|
12 |
+
bl_description = "Renders a small icon that looks very similar to the icons found in official content."
|
13 |
+
bl_options = {'REGISTER', 'UNDO'}
|
14 |
+
|
15 |
+
items = [
|
16 |
+
('FACE_TEXTURE', "With Textures", "", 'FACESEL_HLT', 1),
|
17 |
+
('NOW_MATERIAL', "With Material", "", 'MATERIAL', 2),
|
18 |
+
]
|
19 |
+
mode = bpy.props.EnumProperty(items=items, name="Mode", default='FACE_TEXTURE')
|
20 |
+
|
21 |
+
use_freestyle = bpy.props.BoolProperty(name="Outline", default=True)
|
22 |
+
line_thickness = bpy.props.FloatProperty(name="Outline Thickness", default=0.2, min=0, max=0.5, soft_min=0, soft_max=0.5, step=10, precision=2, subtype='PIXEL')
|
23 |
+
line_color = bpy.props.FloatVectorProperty(name="Outline Color", default=(0, 0, 0), min=0, max=1, soft_min=0, soft_max=1, step=10, precision=2, subtype='COLOR', size=3)
|
24 |
+
|
25 |
+
resolution = bpy.props.IntProperty(name="Resolution", default=80, min=10, max=800, soft_min=10, soft_max=800, subtype='PIXEL')
|
26 |
+
camera_angle = bpy.props.FloatVectorProperty(name="Camera Angle", default=(0.576667, 0.576667, 0.578715), min=-10, max=10, soft_min=-10, soft_max=10, step=1, precision=2, subtype='DIRECTION', size=3)
|
27 |
+
camera_move = bpy.props.FloatVectorProperty(name="Camera Movement", default=(0, 0), min=-10, max=10, soft_min=-10, soft_max=10, step=10, precision=2, subtype='XYZ', size=2)
|
28 |
+
zoom_multi = bpy.props.IntProperty(name="Distance", default=100, min=10, max=190, soft_min=10, soft_max=190, step=10, subtype='PERCENTAGE')
|
29 |
+
|
30 |
+
use_background_color = bpy.props.BoolProperty(name="Use Background", default=True)
|
31 |
+
background_color = bpy.props.FloatVectorProperty(name="Background Color", default=(1, 1, 1), min=0, max=1, soft_min=0, soft_max=1, step=10, precision=2, subtype='COLOR', size=3)
|
32 |
+
is_round_background = bpy.props.BoolProperty(name="Round Corners", default=True)
|
33 |
+
|
34 |
+
layer_image = bpy.props.StringProperty(name="Overlay", default="")
|
35 |
+
|
36 |
+
@classmethod
|
37 |
+
def poll(cls, context):
|
38 |
+
obs = context.selected_objects
|
39 |
+
if not len(obs):
|
40 |
+
return False
|
41 |
+
for ob in obs:
|
42 |
+
if ob.type == 'MESH':
|
43 |
+
return True
|
44 |
+
return False
|
45 |
+
|
46 |
+
def invoke(self, context, event):
|
47 |
+
obs = context.selected_objects
|
48 |
+
for ob in obs:
|
49 |
+
if ob.type != 'MESH': continue
|
50 |
+
me = ob.data
|
51 |
+
if not len(me.uv_textures): continue
|
52 |
+
if me.uv_textures.active.data[0].image:
|
53 |
+
self.mode = 'FACE_TEXTURE'
|
54 |
+
break
|
55 |
+
else:
|
56 |
+
self.mode = 'NOW_MATERIAL'
|
57 |
+
|
58 |
+
if 'render_cm3d2_icon_background_color' in context.scene:
|
59 |
+
try:
|
60 |
+
color = str( context.scene['render_cm3d2_icon_background_color'] ).split(",")
|
61 |
+
if len(color) == 3:
|
62 |
+
self.background_color[0] = float(color[0])
|
63 |
+
self.background_color[1] = float(color[1])
|
64 |
+
self.background_color[2] = float(color[2])
|
65 |
+
except: pass
|
66 |
+
if 'render_cm3d2_icon_background_color_layer_image' in context.scene: self.layer_image = context.scene['render_cm3d2_icon_background_color_layer_image']
|
67 |
+
|
68 |
+
return context.window_manager.invoke_props_dialog(self)
|
69 |
+
|
70 |
+
def draw(self, context):
|
71 |
+
self.layout.prop(self, 'resolution', icon='IMAGE_COL', slider=True)
|
72 |
+
col = self.layout.column(align=True)
|
73 |
+
col.label(text="Texture reference method", icon='TEXTURE_SHADED')
|
74 |
+
row = col.row()
|
75 |
+
row.prop(self, 'mode', icon='TEXTURE_SHADED', expand=True)
|
76 |
+
self.layout.separator()
|
77 |
+
|
78 |
+
row = self.layout.split(percentage=1/3, align=True)
|
79 |
+
row.prop(self, 'use_freestyle', icon='LINE_DATA', text="Outline")
|
80 |
+
row.prop(self, 'line_thickness', icon='ARROW_LEFTRIGHT', slider=True, text="")
|
81 |
+
row.prop(self, 'line_color', icon='COLOR', text="")
|
82 |
+
self.layout.separator()
|
83 |
+
|
84 |
+
col = self.layout.column(align=True)
|
85 |
+
col.label(text="Camera Angle", icon='FILE_REFRESH')
|
86 |
+
col.prop(self, 'camera_angle', text="")
|
87 |
+
self.layout.prop(self, 'camera_move', icon='ARROW_LEFTRIGHT')
|
88 |
+
self.layout.prop(self, 'zoom_multi', icon='VIEWZOOM', slider=True)
|
89 |
+
self.layout.separator()
|
90 |
+
|
91 |
+
row = self.layout.split(percentage=0.333333333, align=True)
|
92 |
+
row.prop(self, 'use_background_color', icon='WORLD')
|
93 |
+
row.prop(self, 'background_color', icon='COLOR', text="")
|
94 |
+
row.prop(self, 'is_round_background', icon='MATCAP_24')
|
95 |
+
|
96 |
+
self.layout.separator()
|
97 |
+
self.layout.prop_search(self, 'layer_image', context.blend_data, "images", icon='MOD_UVPROJECT')
|
98 |
+
|
99 |
+
def execute(self, context):
|
100 |
+
import math, mathutils
|
101 |
+
|
102 |
+
c = self.background_color[:]
|
103 |
+
context.scene['render_cm3d2_icon_background_color'] = ",".join([ str(c[0]), str(c[1]), str(c[2]) ])
|
104 |
+
context.scene['render_cm3d2_icon_background_color_layer_image'] = self.layer_image
|
105 |
+
|
106 |
+
override = context.copy()
|
107 |
+
|
108 |
+
obs = context.selected_objects
|
109 |
+
|
110 |
+
if self.mode == 'FACE_TEXTURE':
|
111 |
+
material_restores = []
|
112 |
+
temp_mates = []
|
113 |
+
for ob in obs:
|
114 |
+
material_restores.append( common.material_restore(ob) )
|
115 |
+
override['object'] = ob
|
116 |
+
bpy.ops.object.material_slot_add(override)
|
117 |
+
temp_mate = context.blend_data.materials.new("temp")
|
118 |
+
ob.material_slots[0].material = temp_mate
|
119 |
+
temp_mate.use_shadeless = True
|
120 |
+
temp_mate.use_face_texture = True
|
121 |
+
temp_mate.use_transparency = True
|
122 |
+
temp_mate.alpha = 0.0
|
123 |
+
temp_mate.use_face_texture_alpha = True
|
124 |
+
temp_mates.append(temp_mate)
|
125 |
+
elif self.mode == 'NOW_MATERIAL':
|
126 |
+
pre_mate_settings = []
|
127 |
+
for ob in obs:
|
128 |
+
pre_mate_settings.append([])
|
129 |
+
for slot in ob.material_slots:
|
130 |
+
if not slot.material: continue
|
131 |
+
mate = slot.material
|
132 |
+
pre_mate_settings[-1].append([mate, mate.use_shadeless])
|
133 |
+
mate.use_shadeless = True
|
134 |
+
|
135 |
+
xs, ys, zs = [], [], []
|
136 |
+
for ob in obs:
|
137 |
+
if ob.type == 'MESH':
|
138 |
+
temp_me = ob.to_mesh(context.scene, apply_modifiers=True, settings='PREVIEW')
|
139 |
+
for vert in temp_me.vertices:
|
140 |
+
co = ob.matrix_world * vert.co
|
141 |
+
xs.append(co.x)
|
142 |
+
ys.append(co.y)
|
143 |
+
zs.append(co.z)
|
144 |
+
common.remove_data(temp_me)
|
145 |
+
center_co = mathutils.Vector((0, 0, 0))
|
146 |
+
center_co.x = (min(xs) + max(xs)) / 2.0
|
147 |
+
center_co.y = (min(ys) + max(ys)) / 2.0
|
148 |
+
center_co.z = (min(zs) + max(zs)) / 2.0
|
149 |
+
|
150 |
+
hide_render_restore = common.hide_render_restore()
|
151 |
+
|
152 |
+
maxs = [-999, -999, -999]
|
153 |
+
mins = [999, 999, 999]
|
154 |
+
for ob in obs:
|
155 |
+
for i in range(8):
|
156 |
+
for j in range(3):
|
157 |
+
v = ob.bound_box[i][j]
|
158 |
+
if maxs[j] < v:
|
159 |
+
maxs[j] = v
|
160 |
+
if v < mins[j]:
|
161 |
+
mins[j] = v
|
162 |
+
|
163 |
+
lens = [maxs[0] - mins[0]]
|
164 |
+
lens.append(maxs[1] - mins[1])
|
165 |
+
lens.append(maxs[2] - mins[2])
|
166 |
+
lens.sort()
|
167 |
+
zoom = lens[-1] * 1.2
|
168 |
+
|
169 |
+
pre_scene_camera = context.scene.camera
|
170 |
+
temp_camera = context.blend_data.cameras.new("render_cm3d2_icon_temp")
|
171 |
+
temp_camera_ob = context.blend_data.objects.new("render_cm3d2_icon_temp", temp_camera)
|
172 |
+
context.scene.objects.link(temp_camera_ob)
|
173 |
+
context.scene.camera = temp_camera_ob
|
174 |
+
temp_camera.type = 'ORTHO'
|
175 |
+
temp_camera.ortho_scale = zoom * (self.zoom_multi * 0.01)
|
176 |
+
|
177 |
+
direct = self.camera_angle.copy()
|
178 |
+
direct.rotate( mathutils.Euler((math.radians(90), 0, 0), 'XYZ') )
|
179 |
+
temp_camera_ob.rotation_mode = 'QUATERNION'
|
180 |
+
temp_camera_ob.rotation_quaternion = direct.to_track_quat('Z', 'Y')
|
181 |
+
temp_camera_ob.location = direct * 10
|
182 |
+
temp_camera_ob.location += center_co
|
183 |
+
vec = mathutils.Vector()
|
184 |
+
vec.x, vec.y = -self.camera_move.x, -self.camera_move.y
|
185 |
+
temp_camera_ob.location += direct.to_track_quat('Z', 'Y') * vec
|
186 |
+
|
187 |
+
context.scene.render.resolution_x = self.resolution
|
188 |
+
context.scene.render.resolution_y = self.resolution
|
189 |
+
context.scene.render.resolution_percentage = 100
|
190 |
+
|
191 |
+
context.scene.world.light_settings.use_ambient_occlusion = False
|
192 |
+
context.scene.world.light_settings.ao_blend_type = 'ADD'
|
193 |
+
context.scene.world.light_settings.gather_method = 'RAYTRACE'
|
194 |
+
context.scene.world.light_settings.samples = 10
|
195 |
+
|
196 |
+
context.scene.render.alpha_mode = 'SKY' if self.use_background_color else 'TRANSPARENT'
|
197 |
+
context.scene.world.horizon_color = self.background_color
|
198 |
+
|
199 |
+
if self.use_freestyle:
|
200 |
+
pre_use_freestyle = context.scene.render.use_freestyle
|
201 |
+
pre_line_thickness = context.scene.render.line_thickness
|
202 |
+
context.scene.render.use_freestyle = True
|
203 |
+
context.scene.render.line_thickness = self.line_thickness
|
204 |
+
temp_lineset = context.scene.render.layers.active.freestyle_settings.linesets.new("temp")
|
205 |
+
temp_lineset.linestyle.color = self.line_color
|
206 |
+
|
207 |
+
# コンポジットノード #
|
208 |
+
pre_use_nodes = context.scene.use_nodes
|
209 |
+
context.scene.use_nodes = True
|
210 |
+
node_tree = context.scene.node_tree
|
211 |
+
for node in node_tree.nodes:
|
212 |
+
node_tree.nodes.remove(node)
|
213 |
+
|
214 |
+
in_node = node_tree.nodes.new('CompositorNodeRLayers')
|
215 |
+
in_node.location = (0, 0)
|
216 |
+
|
217 |
+
img_node = node_tree.nodes.new('CompositorNodeImage')
|
218 |
+
img_node.location = (0, -300)
|
219 |
+
blend_path = os.path.join(os.path.dirname(__file__), "append_data.blend")
|
220 |
+
if "Icon Alpha" in context.blend_data.images:
|
221 |
+
icon_alpha_img = context.blend_data.images["Icon Alpha"]
|
222 |
+
else:
|
223 |
+
with context.blend_data.libraries.load(blend_path) as (data_from, data_to):
|
224 |
+
data_to.images = ["Icon Alpha"]
|
225 |
+
icon_alpha_img = data_to.images[0]
|
226 |
+
img_node.image = icon_alpha_img
|
227 |
+
|
228 |
+
scale_node = node_tree.nodes.new('CompositorNodeScale')
|
229 |
+
scale_node.location = (250, -300)
|
230 |
+
scale_node.space = 'RENDER_SIZE'
|
231 |
+
|
232 |
+
mix_node = node_tree.nodes.new('CompositorNodeMixRGB')
|
233 |
+
mix_node.location = (500, -100)
|
234 |
+
mix_node.blend_type = 'MULTIPLY'
|
235 |
+
|
236 |
+
alpha_node = node_tree.nodes.new('CompositorNodeSetAlpha')
|
237 |
+
alpha_node.location = (750, 0)
|
238 |
+
|
239 |
+
out_node = node_tree.nodes.new('CompositorNodeComposite')
|
240 |
+
out_node.location = (1500, 0)
|
241 |
+
|
242 |
+
layer_img = None
|
243 |
+
if self.layer_image in context.blend_data.images:
|
244 |
+
layer_img = context.blend_data.images[self.layer_image]
|
245 |
+
if layer_img:
|
246 |
+
layer_img_node = node_tree.nodes.new('CompositorNodeImage')
|
247 |
+
layer_img_node.location = (750, -200)
|
248 |
+
layer_img_node.image = layer_img
|
249 |
+
|
250 |
+
layer_scale_node = node_tree.nodes.new('CompositorNodeScale')
|
251 |
+
layer_scale_node.location = (1000, -200)
|
252 |
+
layer_scale_node.space = 'RENDER_SIZE'
|
253 |
+
|
254 |
+
layer_add_node = node_tree.nodes.new('CompositorNodeAlphaOver')
|
255 |
+
layer_add_node.location = (1250, 0)
|
256 |
+
|
257 |
+
node_tree.links.new(layer_add_node.inputs[1], alpha_node.outputs[0])
|
258 |
+
node_tree.links.new(layer_scale_node.inputs[0], layer_img_node.outputs[0])
|
259 |
+
node_tree.links.new(layer_add_node.inputs[2], layer_scale_node.outputs[0])
|
260 |
+
node_tree.links.new(out_node.inputs[0], layer_add_node.outputs[0])
|
261 |
+
else:
|
262 |
+
node_tree.links.new(out_node.inputs[0], alpha_node.outputs[0])
|
263 |
+
|
264 |
+
node_tree.links.new(alpha_node.inputs[0], in_node.outputs[0])
|
265 |
+
node_tree.links.new(mix_node.inputs[1], in_node.outputs[1])
|
266 |
+
node_tree.links.new(scale_node.inputs[0], img_node.outputs[0])
|
267 |
+
node_tree.links.new(mix_node.inputs[2], scale_node.outputs[0])
|
268 |
+
if self.is_round_background:
|
269 |
+
node_tree.links.new(alpha_node.inputs[1], mix_node.outputs[0])
|
270 |
+
# コンポジットノード #
|
271 |
+
|
272 |
+
bpy.ops.render.render()
|
273 |
+
|
274 |
+
if self.is_round_background:
|
275 |
+
for node in node_tree.nodes:
|
276 |
+
node_tree.nodes.remove(node)
|
277 |
+
context.scene.use_nodes = False
|
278 |
+
common.remove_data([icon_alpha_img])
|
279 |
+
|
280 |
+
if self.use_freestyle:
|
281 |
+
context.scene.render.use_freestyle = pre_use_freestyle
|
282 |
+
context.scene.render.line_thickness = pre_line_thickness
|
283 |
+
common.remove_data([temp_lineset.linestyle])
|
284 |
+
context.scene.render.layers.active.freestyle_settings.linesets.remove(temp_lineset)
|
285 |
+
|
286 |
+
img = context.blend_data.images["Render Result"]
|
287 |
+
img['tex Name'] = common.remove_serial_number( context.active_object.name.split('.')[0] ) + "_i_.tex"
|
288 |
+
img['cm3d2_path'] = "assets/texture/texture/" + context.active_object.name.split('.')[0] + "_i_.png"
|
289 |
+
area = common.get_request_area(context, 'IMAGE_EDITOR')
|
290 |
+
common.set_area_space_attr(area, 'image', img)
|
291 |
+
|
292 |
+
common.remove_data([temp_camera_ob, temp_camera])
|
293 |
+
context.scene.camera = pre_scene_camera
|
294 |
+
|
295 |
+
if self.mode == 'FACE_TEXTURE':
|
296 |
+
for material_restore in material_restores:
|
297 |
+
material_restore.restore()
|
298 |
+
common.remove_data(temp_mates)
|
299 |
+
elif self.mode == 'NOW_MATERIAL':
|
300 |
+
for ob_mate in pre_mate_settings:
|
301 |
+
for mate, is_shadeless in ob_mate:
|
302 |
+
mate.use_shadeless = is_shadeless
|
303 |
+
|
304 |
+
hide_render_restore.restore()
|
305 |
+
|
306 |
+
return {'FINISHED'}
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_TEXTURE_PT_context_texture.py
ADDED
@@ -0,0 +1,568 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 「プロパティ」エリア → 「テクスチャ」タブ
|
2 |
+
import os, re, sys, bpy, time, bmesh, mathutils
|
3 |
+
from . import common
|
4 |
+
|
5 |
+
# メニュー等に項目追加
|
6 |
+
def menu_func(self, context):
|
7 |
+
import os
|
8 |
+
|
9 |
+
try:
|
10 |
+
tex_slot = context.texture_slot
|
11 |
+
tex = context.texture
|
12 |
+
mate = context.active_object.active_material
|
13 |
+
mate['shader1']
|
14 |
+
mate['shader2']
|
15 |
+
except: return
|
16 |
+
if not tex_slot: return
|
17 |
+
|
18 |
+
if tex_slot.use:
|
19 |
+
type = 'tex'
|
20 |
+
else:
|
21 |
+
type = 'col' if tex_slot.use_rgb_to_intensity else 'f'
|
22 |
+
base_name = common.remove_serial_number(tex.name)
|
23 |
+
|
24 |
+
box = self.layout.box()
|
25 |
+
box.label(text="For CM3D2", icon_value=common.preview_collections['main']['KISS'].icon_id)
|
26 |
+
split = box.split(percentage=0.333333333333333333)
|
27 |
+
split.label(text="Setting type:")
|
28 |
+
row = split.row()
|
29 |
+
|
30 |
+
if type == 'tex': row.label(text='Texture', icon='TEXTURE')
|
31 |
+
elif type == 'col': row.label(text='Color', icon='COLOR')
|
32 |
+
elif type == 'f': row.label(text='Value', icon='ARROW_LEFTRIGHT')
|
33 |
+
|
34 |
+
check_row = row.row(align=True)
|
35 |
+
check_row.prop(tex_slot, 'use', text="")
|
36 |
+
sub_row = check_row.row()
|
37 |
+
sub_row.prop(tex_slot, 'use_rgb_to_intensity', text="")
|
38 |
+
if tex_slot.use:
|
39 |
+
sub_row.enabled = False
|
40 |
+
box.prop(tex, 'name', icon='SORTALPHA', text="Setting value name")
|
41 |
+
|
42 |
+
if type == "tex":
|
43 |
+
if tex.type == 'IMAGE':
|
44 |
+
img = tex.image
|
45 |
+
if img:
|
46 |
+
if img.source == 'FILE':
|
47 |
+
|
48 |
+
if re.search(r"\.[Pp][Nn][Gg]$", img.name):
|
49 |
+
img.name = re.sub(r"\.[Pp][Nn][Gg]$", "", img.name)
|
50 |
+
if re.search(r"\.[Pp][Nn][Gg]\.\d{3}$", img.name):
|
51 |
+
img.name = re.sub(r"\.[Pp][Nn][Gg](\.\d{3})$", r"\1", img.name)
|
52 |
+
|
53 |
+
sub_box = box.box()
|
54 |
+
row = sub_box.split(percentage=0.333333333333, align=True)
|
55 |
+
row.label(text="Texture name:")
|
56 |
+
row.template_ID(tex, 'image', open='image.open')
|
57 |
+
if 'cm3d2_path' not in img:
|
58 |
+
img['cm3d2_path'] = "Assets\\texture\\texture\\" + os.path.basename(img.filepath)
|
59 |
+
sub_box.prop(img, '["cm3d2_path"]', text="Texture path")
|
60 |
+
|
61 |
+
if base_name == "_ToonRamp":
|
62 |
+
sub_box.menu('TEXTURE_PT_context_texture_ToonRamp', icon='NLA')
|
63 |
+
elif base_name == "_ShadowRateToon":
|
64 |
+
sub_box.menu('TEXTURE_PT_context_texture_ShadowRateToon', icon='NLA')
|
65 |
+
split = sub_box.split(percentage=0.333333333333, align=True)
|
66 |
+
split.label(text="Offset:")
|
67 |
+
row = split.row(align=True)
|
68 |
+
row.prop(tex_slot, 'color', index=0, text="")
|
69 |
+
row.prop(tex_slot, 'color', index=1, text="")
|
70 |
+
|
71 |
+
split = sub_box.split(percentage=0.333333333333, align=True)
|
72 |
+
split.label(text="Enlargement/Reduction:")
|
73 |
+
row = split.row(align=True)
|
74 |
+
row.prop(tex_slot, 'color', index=2, text="")
|
75 |
+
row.prop(tex_slot, 'diffuse_color_factor', text="")
|
76 |
+
|
77 |
+
row = sub_box.row()
|
78 |
+
row.operator('image.show_image', text="Image to UV/Image Editor", icon='ZOOM_IN').image_name = img.name
|
79 |
+
if len(img.pixels):
|
80 |
+
row.operator('image.quick_export_cm3d2_tex', text="Save as .Tex", icon='FILESEL')
|
81 |
+
else:
|
82 |
+
row.operator('image.replace_cm3d2_tex', icon='BORDERMOVE')
|
83 |
+
|
84 |
+
elif type == "col":
|
85 |
+
sub_box = box.box()
|
86 |
+
|
87 |
+
#row = sub_box.split(percentage=0.7, align=True)
|
88 |
+
row = sub_box.row(align=True)
|
89 |
+
row.prop(tex_slot, 'color', text="")
|
90 |
+
row.operator('texture.auto_set_color_value', icon='RECOVER_AUTO', text="Auto")
|
91 |
+
row.operator('texture.set_color_value', text="", icon='MATCAP_10').color = [0, 0, 0] + [tex_slot.diffuse_color_factor]
|
92 |
+
row.operator('texture.set_color_value', text="", icon='MATCAP_24').color = [1, 1, 1] + [tex_slot.diffuse_color_factor]
|
93 |
+
|
94 |
+
row = sub_box.row(align=True)
|
95 |
+
row.operator('texture.set_color_value', text="", icon='TRIA_LEFT').color = list(tex_slot.color) + [0]
|
96 |
+
row.prop(tex_slot, 'diffuse_color_factor', icon='IMAGE_RGB_ALPHA', text="Alpha", slider=True)
|
97 |
+
row.operator('texture.set_color_value', text="", icon='TRIA_RIGHT').color = list(tex_slot.color) + [1]
|
98 |
+
|
99 |
+
elif type == "f":
|
100 |
+
sub_box = box.box()
|
101 |
+
row = sub_box.row(align=True)
|
102 |
+
row.prop(tex_slot, 'diffuse_color_factor', icon='ARROW_LEFTRIGHT', text="Value")
|
103 |
+
|
104 |
+
data_path = 'texture_slot.diffuse_color_factor'
|
105 |
+
if base_name == '_Shininess':
|
106 |
+
row.menu('TEXTURE_PT_context_texture_values_normal', icon='DOWNARROW_HLT', text="")
|
107 |
+
|
108 |
+
row = sub_box.row(align=True)
|
109 |
+
row.operator('texture.set_color_value', text="0.0", icon='MATCAP_10').color = list(tex_slot.color) + [0.0]
|
110 |
+
row.operator('texture.set_color_value', text="0.25").color = list(tex_slot.color) + [0.25]
|
111 |
+
row.operator('texture.set_color_value', text="0.5").color = list(tex_slot.color) + [0.5]
|
112 |
+
row.operator('texture.set_color_value', text="0.75").color = list(tex_slot.color) + [0.75]
|
113 |
+
row.operator('texture.set_color_value', text="1.0", icon='MATCAP_09').color = list(tex_slot.color) + [1.0]
|
114 |
+
|
115 |
+
elif base_name == '_OutlineWidth':
|
116 |
+
row.menu('TEXTURE_PT_context_texture_values_OutlineWidth', icon='DOWNARROW_HLT', text="")
|
117 |
+
|
118 |
+
row = sub_box.row(align=True)
|
119 |
+
row.operator('texture.set_color_value', text="0.001", icon='MATSPHERE').color = list(tex_slot.color) + [0.001]
|
120 |
+
row.operator('texture.set_color_value', text="0.0015").color = list(tex_slot.color) + [0.0015]
|
121 |
+
row.operator('texture.set_color_value', text="0.002", icon='ANTIALIASED').color = list(tex_slot.color) + [0.002]
|
122 |
+
|
123 |
+
split = sub_box.split(percentage=0.3)
|
124 |
+
split.label(text=" Exact Value: ")
|
125 |
+
split.label(text=str(tex_slot.diffuse_color_factor))
|
126 |
+
|
127 |
+
elif base_name == '_RimPower':
|
128 |
+
row.menu('TEXTURE_PT_context_texture_values_RimPower', icon='DOWNARROW_HLT', text="")
|
129 |
+
|
130 |
+
row = sub_box.row(align=True)
|
131 |
+
row.operator('texture.set_color_value', text="1", icon='BRUSH_TEXFILL').color = list(tex_slot.color) + [1]
|
132 |
+
row.operator('texture.set_color_value', text="10").color = list(tex_slot.color) + [10]
|
133 |
+
row.operator('texture.set_color_value', text="20").color = list(tex_slot.color) + [20]
|
134 |
+
row.operator('texture.set_color_value', text="30", icon='MATCAP_07').color = list(tex_slot.color) + [30]
|
135 |
+
|
136 |
+
elif base_name == '_RimShift':
|
137 |
+
row.menu('TEXTURE_PT_context_texture_values_normal', icon='DOWNARROW_HLT', text="")
|
138 |
+
|
139 |
+
row = sub_box.row(align=True)
|
140 |
+
row.operator('texture.set_color_value', text="0.0", icon='FULLSCREEN_EXIT').color = list(tex_slot.color) + [0.0]
|
141 |
+
row.operator('texture.set_color_value', text="0.25").color = list(tex_slot.color) + [0.25]
|
142 |
+
row.operator('texture.set_color_value', text="0.5").color = list(tex_slot.color) + [0.5]
|
143 |
+
row.operator('texture.set_color_value', text="0.75").color = list(tex_slot.color) + [0.75]
|
144 |
+
row.operator('texture.set_color_value', text="1.0", icon='FULLSCREEN_ENTER').color = list(tex_slot.color) + [1.0]
|
145 |
+
|
146 |
+
box.operator('texture.sync_tex_color_ramps', icon='LINKED')
|
147 |
+
|
148 |
+
description = ""
|
149 |
+
if base_name == '_MainTex':
|
150 |
+
description = ["The Main Texture is the primary texture as it will be most visible on the surface of the mesh.", "The Texture path should move automatically at the time of export.", "Please insert an appropriate texture name and withold any file type extensions."]
|
151 |
+
if base_name == '_ToonRamp':
|
152 |
+
description = ["This specifies a toon ramp that will be used in the transition from lit areas to shadow areas."]
|
153 |
+
elif base_name == '_ShadowTex':
|
154 |
+
description = ["The texture will determine the Color of the surface of the shaded portion.", "Specify the range in the _ShadowRateToon。"]
|
155 |
+
if base_name == '_ShadowRateToon':
|
156 |
+
description = ["Will affect the intensity of the Shadowtex.", "This Makes black colors valid and white colors invalid"]
|
157 |
+
elif base_name == '_Color':
|
158 |
+
description = ["Specifies the color of the surface", "This setting has no effect when the color is white."]
|
159 |
+
elif base_name == '_ShadowColor':
|
160 |
+
description = ["Specifies the color of the shadow. Turn it white to disable it", "This will determine the color of the shadow any object casts on it."]
|
161 |
+
elif base_name == '_RimColor':
|
162 |
+
description = ["Specify the color of the Rim light", "The Rim light is the reflection of light that is on the edge", "It is recommended that you turn this off by setting the color to black and setting transparency to 0.", "Alternatively, you can use it though you should use near black color values."]
|
163 |
+
elif base_name == '_OutlineColor':
|
164 |
+
description = ["Specifies the Outline's color", "It is advised that the color you choose be darker or lighter then your texture."]
|
165 |
+
elif base_name == '_Shininess':
|
166 |
+
description = ["Strength of the Shininess", "This is good for simulating reflective surfaces like Metals, Leathers, and Glass."]
|
167 |
+
elif base_name == '_OutlineWidth':
|
168 |
+
description = ["Specifies the Outline's width", "0.002 is thick、0.001 is narrow。"]
|
169 |
+
elif base_name == '_RimPower':
|
170 |
+
description = ["Specifies Rim Lighting Power", "This value is often found to be around 10.", "If the value is close to 0. then the Rim lighting will not appear properly.", "Rim Power determines how the Rim light fades at it's ends. Higher values make the fade less subtle."]
|
171 |
+
elif base_name == '_RimShift':
|
172 |
+
description = ["Specifies the Size of the Rim Light", "At a value of 1. your mesh is completely engulfed by the Rim light.", "At a value of -0.1 the Rim light disappears."]
|
173 |
+
elif base_name == '_RenderTex':
|
174 |
+
description = ["Sets the Mosiac shader value", "This should be left alone."]
|
175 |
+
elif base_name == '_FloatValue1':
|
176 |
+
description = ["The amount of pixels in the Mosiac.", "Larger values give you a clearer picture."]
|
177 |
+
|
178 |
+
if description != "":
|
179 |
+
sub_box = box.box()
|
180 |
+
col = sub_box.column(align=True)
|
181 |
+
col.label(text="Comments", icon='TEXT')
|
182 |
+
for line in description:
|
183 |
+
col.label(text=line)
|
184 |
+
|
185 |
+
# _ToonRamp設定メニュー
|
186 |
+
class TEXTURE_PT_context_texture_ToonRamp(bpy.types.Menu):
|
187 |
+
bl_idname = 'TEXTURE_PT_context_texture_ToonRamp'
|
188 |
+
bl_label = "_ToonRamp Configuration"
|
189 |
+
|
190 |
+
def draw(self, context):
|
191 |
+
l = self.layout
|
192 |
+
cmd = 'texture.set_default_toon_textures'
|
193 |
+
l.operator(cmd, text="NoTex", icon='SPACE2').name = "NoTex"
|
194 |
+
l.operator(cmd, text="ToonBlackA1", icon='SPACE2').name = "ToonBlackA1"
|
195 |
+
l.operator(cmd, text="ToonBlueA1", icon='SPACE2').name = "ToonBlueA1"
|
196 |
+
l.operator(cmd, text="ToonBlueA2", icon='SPACE2').name = "ToonBlueA2"
|
197 |
+
l.operator(cmd, text="ToonBrownA1", icon='SPACE2').name = "ToonBrownA1"
|
198 |
+
l.operator(cmd, text="ToonDress_Shadow", icon='LAYER_USED').name = "ToonDress_Shadow"
|
199 |
+
l.operator(cmd, text="ToonFace", icon='SPACE2').name = "ToonFace"
|
200 |
+
l.operator(cmd, text="ToonFace_Shadow", icon='LAYER_USED').name = "ToonFace_Shadow"
|
201 |
+
l.operator(cmd, text="ToonFace002", icon='SPACE2').name = "ToonFace002"
|
202 |
+
l.operator(cmd, text="ToonGrayA1", icon='SPACE2').name = "ToonGrayA1"
|
203 |
+
l.operator(cmd, text="ToonGreenA1", icon='SPACE2').name = "ToonGreenA1"
|
204 |
+
l.operator(cmd, text="ToonGreenA2", icon='SPACE2').name = "ToonGreenA2"
|
205 |
+
l.operator(cmd, text="ToonOrangeA1", icon='SPACE2').name = "ToonOrangeA1"
|
206 |
+
l.operator(cmd, text="ToonPinkA1", icon='SPACE2').name = "ToonPinkA1"
|
207 |
+
l.operator(cmd, text="ToonPinkA2", icon='SPACE2').name = "ToonPinkA2"
|
208 |
+
l.operator(cmd, text="ToonPurpleA1", icon='SPACE2').name = "ToonPurpleA1"
|
209 |
+
l.operator(cmd, text="ToonRedA1", icon='SPACE2').name = "ToonRedA1"
|
210 |
+
l.operator(cmd, text="ToonRedA2", icon='SPACE2').name = "ToonRedA2"
|
211 |
+
l.operator(cmd, text="ToonSkin", icon='SPACE2').name = "ToonSkin"
|
212 |
+
l.operator(cmd, text="ToonSkin_Shadow", icon='LAYER_USED').name = "ToonSkin_Shadow"
|
213 |
+
l.operator(cmd, text="ToonSkin002", icon='SPACE2').name = "ToonSkin002"
|
214 |
+
l.operator(cmd, text="ToonYellowA1", icon='SPACE2').name = "ToonYellowA1"
|
215 |
+
l.operator(cmd, text="ToonYellowA2", icon='SPACE2').name = "ToonYellowA2"
|
216 |
+
l.operator(cmd, text="ToonYellowA3", icon='SPACE2').name = "ToonYellowA3"
|
217 |
+
|
218 |
+
# _ShadowRateToon設定メニュー
|
219 |
+
class TEXTURE_PT_context_texture_ShadowRateToon(bpy.types.Menu):
|
220 |
+
bl_idname = 'TEXTURE_PT_context_texture_ShadowRateToon'
|
221 |
+
bl_label = "_ShadowRateToon Configuration"
|
222 |
+
|
223 |
+
def draw(self, context):
|
224 |
+
l = self.layout
|
225 |
+
cmd = 'texture.set_default_toon_textures'
|
226 |
+
l.operator(cmd, text="NoTex", icon='LAYER_USED').name = "NoTex"
|
227 |
+
l.operator(cmd, text="ToonBlackA1", icon='LAYER_USED').name = "ToonBlackA1"
|
228 |
+
l.operator(cmd, text="ToonBlueA1", icon='LAYER_USED').name = "ToonBlueA1"
|
229 |
+
l.operator(cmd, text="ToonBlueA2", icon='LAYER_USED').name = "ToonBlueA2"
|
230 |
+
l.operator(cmd, text="ToonBrownA1", icon='LAYER_USED').name = "ToonBrownA1"
|
231 |
+
l.operator(cmd, text="ToonDress_Shadow", icon='SPACE2').name = "ToonDress_Shadow"
|
232 |
+
l.operator(cmd, text="ToonFace", icon='LAYER_USED').name = "ToonFace"
|
233 |
+
l.operator(cmd, text="ToonFace_Shadow", icon='SPACE2').name = "ToonFace_Shadow"
|
234 |
+
l.operator(cmd, text="ToonFace002", icon='LAYER_USED').name = "ToonFace002"
|
235 |
+
l.operator(cmd, text="ToonGrayA1", icon='LAYER_USED').name = "ToonGrayA1"
|
236 |
+
l.operator(cmd, text="ToonGreenA1", icon='LAYER_USED').name = "ToonGreenA1"
|
237 |
+
l.operator(cmd, text="ToonGreenA2", icon='LAYER_USED').name = "ToonGreenA2"
|
238 |
+
l.operator(cmd, text="ToonOrangeA1", icon='LAYER_USED').name = "ToonOrangeA1"
|
239 |
+
l.operator(cmd, text="ToonPinkA1", icon='LAYER_USED').name = "ToonPinkA1"
|
240 |
+
l.operator(cmd, text="ToonPinkA2", icon='LAYER_USED').name = "ToonPinkA2"
|
241 |
+
l.operator(cmd, text="ToonPurpleA1", icon='LAYER_USED').name = "ToonPurpleA1"
|
242 |
+
l.operator(cmd, text="ToonRedA1", icon='LAYER_USED').name = "ToonRedA1"
|
243 |
+
l.operator(cmd, text="ToonRedA2", icon='LAYER_USED').name = "ToonRedA2"
|
244 |
+
l.operator(cmd, text="ToonSkin", icon='LAYER_USED').name = "ToonSkin"
|
245 |
+
l.operator(cmd, text="ToonSkin_Shadow", icon='SPACE2').name = "ToonSkin_Shadow"
|
246 |
+
l.operator(cmd, text="ToonSkin002", icon='LAYER_USED').name = "ToonSkin002"
|
247 |
+
l.operator(cmd, text="ToonYellowA1", icon='LAYER_USED').name = "ToonYellowA1"
|
248 |
+
l.operator(cmd, text="ToonYellowA2", icon='LAYER_USED').name = "ToonYellowA2"
|
249 |
+
l.operator(cmd, text="ToonYellowA3", icon='LAYER_USED').name = "ToonYellowA3"
|
250 |
+
|
251 |
+
# 0.0~1.0までの値設定メニュー
|
252 |
+
class TEXTURE_PT_context_texture_values_normal(bpy.types.Menu):
|
253 |
+
bl_idname = 'TEXTURE_PT_context_texture_values_normal'
|
254 |
+
bl_label = "Value list"
|
255 |
+
|
256 |
+
def draw(self, context):
|
257 |
+
tex_slot = context.texture_slot
|
258 |
+
for i in range(11):
|
259 |
+
value = round(i * 0.1, 1)
|
260 |
+
icon = 'LAYER_USED' if i % 2 else 'LAYER_ACTIVE'
|
261 |
+
self.layout.operator('texture.set_color_value', text=str(value), icon=icon).color = list(tex_slot.color) + [value]
|
262 |
+
|
263 |
+
# _OutlineWidth用の値設定メニュー
|
264 |
+
class TEXTURE_PT_context_texture_values_OutlineWidth(bpy.types.Menu):
|
265 |
+
bl_idname = 'TEXTURE_PT_context_texture_values_OutlineWidth'
|
266 |
+
bl_label = "Value list"
|
267 |
+
|
268 |
+
def draw(self, context):
|
269 |
+
tex_slot = context.texture_slot
|
270 |
+
for i in range(16):
|
271 |
+
value = round(i * 0.0002, 4)
|
272 |
+
icon = 'LAYER_USED' if i % 2 else 'LAYER_ACTIVE'
|
273 |
+
self.layout.operator('texture.set_color_value', text=str(value), icon=icon).color = list(tex_slot.color) + [value]
|
274 |
+
|
275 |
+
# _RimPower用のValue設定メニュー
|
276 |
+
class TEXTURE_PT_context_texture_values_RimPower(bpy.types.Menu):
|
277 |
+
bl_idname = 'TEXTURE_PT_context_texture_values_RimPower'
|
278 |
+
bl_label = "Value list"
|
279 |
+
|
280 |
+
def draw(self, context):
|
281 |
+
tex_slot = context.texture_slot
|
282 |
+
for i in range(16):
|
283 |
+
value = round(i * 2, 0)
|
284 |
+
icon = 'LAYER_USED' if i % 2 else 'LAYER_ACTIVE'
|
285 |
+
if value == 0:
|
286 |
+
icon = 'ERROR'
|
287 |
+
self.layout.operator('texture.set_color_value', text=str(value), icon=icon).color = list(tex_slot.color) + [value]
|
288 |
+
|
289 |
+
class show_image(bpy.types.Operator):
|
290 |
+
bl_idname = 'image.show_image'
|
291 |
+
bl_label = "Open Image in UV/Image Editor"
|
292 |
+
bl_description = "Displays the specified image in the UV/ Image Editor"
|
293 |
+
bl_options = {'REGISTER', 'UNDO'}
|
294 |
+
|
295 |
+
image_name = bpy.props.StringProperty(name="Image name")
|
296 |
+
|
297 |
+
def execute(self, context):
|
298 |
+
if self.image_name in context.blend_data.images:
|
299 |
+
img = context.blend_data.images[self.image_name]
|
300 |
+
else:
|
301 |
+
self.report(type={'ERROR'}, message="Cannot find the file specified")
|
302 |
+
return {'CANCELLED'}
|
303 |
+
|
304 |
+
area = common.get_request_area(context, 'IMAGE_EDITOR')
|
305 |
+
if area:
|
306 |
+
common.set_area_space_attr(area, 'image', img)
|
307 |
+
else:
|
308 |
+
self.report(type={'ERROR'}, message="Area to view the image as not found")
|
309 |
+
return {'CANCELLED'}
|
310 |
+
return {'FINISHED'}
|
311 |
+
|
312 |
+
class replace_cm3d2_tex(bpy.types.Operator):
|
313 |
+
bl_idname = 'image.replace_cm3d2_tex'
|
314 |
+
bl_label = "Find texture"
|
315 |
+
bl_description = "looks for textures in the search paths specified in the Converter Settings."
|
316 |
+
bl_options = {'REGISTER', 'UNDO'}
|
317 |
+
|
318 |
+
@classmethod
|
319 |
+
def poll(cls, context):
|
320 |
+
if 'texture' in dir(context):
|
321 |
+
tex = context.texture
|
322 |
+
return 'image' in dir(tex)
|
323 |
+
return False
|
324 |
+
|
325 |
+
def execute(self, context):
|
326 |
+
tex = context.texture
|
327 |
+
img = tex.image
|
328 |
+
if not common.replace_cm3d2_tex(img):
|
329 |
+
self.report(type={'ERROR'}, message="Could not be located")
|
330 |
+
return {'CANCELLED'}
|
331 |
+
tex.image_user.use_auto_refresh = True
|
332 |
+
return {'FINISHED'}
|
333 |
+
|
334 |
+
class sync_tex_color_ramps(bpy.types.Operator):
|
335 |
+
bl_idname = 'texture.sync_tex_color_ramps'
|
336 |
+
bl_label = "Sync Textures to Colors and Values."
|
337 |
+
bl_description = "Applies Textures according to Color changes. (Example: Changing the RimColor)"
|
338 |
+
bl_options = {'REGISTER', 'UNDO'}
|
339 |
+
|
340 |
+
@classmethod
|
341 |
+
def poll(cls, context):
|
342 |
+
if 'material' in dir(context):
|
343 |
+
if context.material:
|
344 |
+
return True
|
345 |
+
if 'texture_slot' in dir(context) and 'texture' in dir(context):
|
346 |
+
return context.texture_slot and context.texture
|
347 |
+
return False
|
348 |
+
|
349 |
+
def execute(self, context):
|
350 |
+
for mate in context.blend_data.materials:
|
351 |
+
if 'shader1' in mate and 'shader2' in mate:
|
352 |
+
for slot in mate.texture_slots:
|
353 |
+
if not slot:
|
354 |
+
continue
|
355 |
+
common.set_texture_color(slot)
|
356 |
+
return {'FINISHED'}
|
357 |
+
|
358 |
+
class set_default_toon_textures(bpy.types.Operator):
|
359 |
+
bl_idname = 'texture.set_default_toon_textures'
|
360 |
+
bl_label = "Select the toon"
|
361 |
+
bl_description = "Select the default toon texture."
|
362 |
+
bl_options = {'REGISTER', 'UNDO'}
|
363 |
+
|
364 |
+
name = bpy.props.StringProperty(name="Texture name")
|
365 |
+
#dir = bpy.props.StringProperty(name="パス", default="Assets\\texture\\texture\\toon\\")
|
366 |
+
|
367 |
+
@classmethod
|
368 |
+
def poll(cls, context):
|
369 |
+
if 'texture_slot' in dir(context) and 'texture' in dir(context):
|
370 |
+
if context.texture_slot and context.texture:
|
371 |
+
name = common.remove_serial_number(context.texture.name)
|
372 |
+
return name == "_ToonRamp" or name == "_ShadowRateToon"
|
373 |
+
return False
|
374 |
+
|
375 |
+
def execute(self, context):
|
376 |
+
import os.path, struct
|
377 |
+
img = context.texture.image
|
378 |
+
img.name = self.name
|
379 |
+
|
380 |
+
png_path = os.path.join( os.path.dirname(bpy.path.abspath(img.filepath)), self.name + ".png" )
|
381 |
+
tex_path = os.path.splitext(png_path)[0] + ".tex"
|
382 |
+
if not os.path.exists(png_path):
|
383 |
+
if os.path.exists(tex_path):
|
384 |
+
tex_file = open(tex_path, 'rb')
|
385 |
+
header_ext = common.read_str(tex_file)
|
386 |
+
if header_ext == 'CM3D2_TEX':
|
387 |
+
tex_file.seek(4, 1)
|
388 |
+
common.read_str(tex_file)
|
389 |
+
png_size = struct.unpack('<i', tex_file.read(4))[0]
|
390 |
+
png_file = open(png_path, 'wb')
|
391 |
+
png_file.write(tex_file.read(png_size))
|
392 |
+
png_file.close()
|
393 |
+
tex_file.close()
|
394 |
+
img.filepath = png_path
|
395 |
+
img.reload()
|
396 |
+
|
397 |
+
img['cm3d2_path'] = bpy.path.abspath(img.filepath)
|
398 |
+
return {'FINISHED'}
|
399 |
+
|
400 |
+
class auto_set_color_value(bpy.types.Operator):
|
401 |
+
bl_idname = 'texture.auto_set_color_value'
|
402 |
+
bl_label = "Automatically set the Color setting Value"
|
403 |
+
bl_description = "Color settings are set automatically with the Colors in the Texture"
|
404 |
+
bl_options = {'REGISTER', 'UNDO'}
|
405 |
+
|
406 |
+
is_all = bpy.props.BoolProperty(name="All", default=True)
|
407 |
+
saturation_multi = bpy.props.FloatProperty(name="Saturation Multiplication", default=2.2, min=0, max=5, soft_min=0, soft_max=5, step=10, precision=2)
|
408 |
+
value_multi = bpy.props.FloatProperty(name="Mulitplication of Lightness value", default=0.3, min=0, max=5, soft_min=0, soft_max=5, step=10, precision=2)
|
409 |
+
|
410 |
+
@classmethod
|
411 |
+
def poll(cls, context):
|
412 |
+
ob = context.active_object
|
413 |
+
if not ob: return False
|
414 |
+
if ob.type != 'MESH': return False
|
415 |
+
me = ob.data
|
416 |
+
mate = ob.active_material
|
417 |
+
if not mate: return False
|
418 |
+
for slot in mate.texture_slots:
|
419 |
+
if not slot: continue
|
420 |
+
tex = slot.texture
|
421 |
+
name = common.remove_serial_number(tex.name)
|
422 |
+
if name == '_MainTex':
|
423 |
+
img = tex.image
|
424 |
+
if img:
|
425 |
+
if len(img.pixels):
|
426 |
+
break
|
427 |
+
if me.uv_textures.active:
|
428 |
+
if me.uv_textures.active.data[0].image:
|
429 |
+
if len(me.uv_textures.active.data[0].image.pixels):
|
430 |
+
break
|
431 |
+
else: return False
|
432 |
+
if 'texture_slot' in dir(context) and 'texture' in dir(context):
|
433 |
+
slot = context.texture_slot
|
434 |
+
tex = context.texture
|
435 |
+
name = common.remove_serial_number(tex.name)
|
436 |
+
if name in ['_ShadowColor', '_RimColor', '_OutlineColor']:
|
437 |
+
return True
|
438 |
+
return False
|
439 |
+
|
440 |
+
def invoke(self, context, event):
|
441 |
+
return context.window_manager.invoke_props_dialog(self)
|
442 |
+
|
443 |
+
def draw(self, context):
|
444 |
+
self.layout.prop(self, 'is_all', icon='ACTION')
|
445 |
+
row = self.layout.row()
|
446 |
+
row.label(text="", icon='SMOOTH')
|
447 |
+
row.prop(self, 'saturation_multi')
|
448 |
+
row = self.layout.row()
|
449 |
+
row.label(text="", icon='SOLID')
|
450 |
+
row.prop(self, 'value_multi')
|
451 |
+
|
452 |
+
def execute(self, context):
|
453 |
+
ob = context.active_object
|
454 |
+
me = ob.data
|
455 |
+
mate = ob.active_material
|
456 |
+
active_slot = context.texture_slot
|
457 |
+
active_tex = context.texture
|
458 |
+
tex_name = common.remove_serial_number(active_tex.name)
|
459 |
+
|
460 |
+
target_slots = []
|
461 |
+
if self.is_all:
|
462 |
+
for slot in mate.texture_slots:
|
463 |
+
if not slot: continue
|
464 |
+
name = common.remove_serial_number(slot.texture.name)
|
465 |
+
if name in ['_ShadowColor', '_RimColor', '_OutlineColor']:
|
466 |
+
target_slots.append(slot)
|
467 |
+
else:
|
468 |
+
target_slots.append(active_slot)
|
469 |
+
|
470 |
+
for slot in mate.texture_slots:
|
471 |
+
if not slot: continue
|
472 |
+
name = common.remove_serial_number(slot.texture.name)
|
473 |
+
if name == '_MainTex':
|
474 |
+
img = slot.texture.image
|
475 |
+
if img:
|
476 |
+
if len(img.pixels):
|
477 |
+
break
|
478 |
+
else:
|
479 |
+
img = me.uv_textures.active.data[0].image
|
480 |
+
|
481 |
+
sample_count = 10
|
482 |
+
img_width, img_height, img_channel = img.size[0], img.size[1], img.channels
|
483 |
+
|
484 |
+
bm = bmesh.new()
|
485 |
+
bm.from_mesh(me)
|
486 |
+
uv_lay = bm.loops.layers.uv.active
|
487 |
+
uvs = [l[uv_lay].uv[:] for f in bm.faces if f.material_index == ob.active_material_index for l in f.loops]
|
488 |
+
bm.free()
|
489 |
+
|
490 |
+
average_color = mathutils.Color([0, 0, 0])
|
491 |
+
seek_interval = len(uvs) / sample_count
|
492 |
+
for sample_index in range(sample_count):
|
493 |
+
|
494 |
+
uv_index = int(seek_interval * sample_index)
|
495 |
+
x, y = uvs[uv_index]
|
496 |
+
x, y = int(x * img_width), int(y * img_height)
|
497 |
+
|
498 |
+
pixel_index = ((y * img_width) + x) * img_channel
|
499 |
+
color = mathutils.Color(img.pixels[pixel_index:pixel_index+3])
|
500 |
+
|
501 |
+
average_color += color
|
502 |
+
average_color /= sample_count
|
503 |
+
average_color.s *= self.saturation_multi
|
504 |
+
average_color.v *= self.value_multi
|
505 |
+
|
506 |
+
for slot in target_slots:
|
507 |
+
slot.color = average_color[:3]
|
508 |
+
common.set_texture_color(slot)
|
509 |
+
|
510 |
+
return {'FINISHED'}
|
511 |
+
|
512 |
+
class quick_export_cm3d2_tex(bpy.types.Operator):
|
513 |
+
bl_idname = 'image.quick_export_cm3d2_tex'
|
514 |
+
bl_label = "Export As .tex"
|
515 |
+
bl_description = "Save image as a .tex for CM3D2."
|
516 |
+
bl_options = {'REGISTER'}
|
517 |
+
|
518 |
+
def execute(self, context):
|
519 |
+
import os.path
|
520 |
+
|
521 |
+
try:
|
522 |
+
slot = context.texture_slot
|
523 |
+
tex = context.texture
|
524 |
+
img = tex.image
|
525 |
+
img.pixels[0]
|
526 |
+
except:
|
527 |
+
self.report(type={'ERROR'}, message="Mission failed.")
|
528 |
+
return {'CANCELLED'}
|
529 |
+
|
530 |
+
override = context.copy()
|
531 |
+
override['edit_image'] = img
|
532 |
+
filepath = os.path.splitext( bpy.path.abspath(img.filepath) )[0] + ".tex"
|
533 |
+
path = "assets/texture/texture/" + os.path.basename( bpy.path.abspath(img.filepath) )
|
534 |
+
if 'cm3d2_path' in img:
|
535 |
+
path = img['cm3d2_path']
|
536 |
+
if os.path.exists(filepath):
|
537 |
+
file = open(filepath, 'rb')
|
538 |
+
header_ext = common.read_str(file)
|
539 |
+
if header_ext == 'CM3D2_TEX':
|
540 |
+
file.seek(4, 1)
|
541 |
+
path = common.read_str(file)
|
542 |
+
file.close()
|
543 |
+
bpy.ops.image.export_cm3d2_tex(override, filepath=filepath, path=path)
|
544 |
+
|
545 |
+
self.report(type={'INFO'}, message="Texture has been saved as data in the same folder. Mission Accomplished")
|
546 |
+
return {'FINISHED'}
|
547 |
+
|
548 |
+
class set_color_value(bpy.types.Operator):
|
549 |
+
bl_idname = 'texture.set_color_value'
|
550 |
+
bl_label = "Set the Color setting Value"
|
551 |
+
bl_description = "Set the Color type of setting Value"
|
552 |
+
bl_options = {'REGISTER', 'UNDO'}
|
553 |
+
|
554 |
+
color = bpy.props.FloatVectorProperty(name="Color", default=(0, 0, 0, 0), subtype='COLOR', size=4)
|
555 |
+
|
556 |
+
@classmethod
|
557 |
+
def poll(cls, context):
|
558 |
+
if 'texture_slot' in dir(context) and 'texture' in dir(context):
|
559 |
+
if context.texture_slot and context.texture:
|
560 |
+
return True
|
561 |
+
return False
|
562 |
+
|
563 |
+
def execute(self, context):
|
564 |
+
slot = context.texture_slot
|
565 |
+
slot.color = self.color[:3]
|
566 |
+
slot.diffuse_color_factor = self.color[3]
|
567 |
+
common.set_texture_color(slot)
|
568 |
+
return {'FINISHED'}
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_TEXT_HT_header.py
ADDED
@@ -0,0 +1,174 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 「テキストエディター」エリア → ヘッダー
|
2 |
+
import os, re, sys, bpy, time, bmesh, mathutils
|
3 |
+
from . import common
|
4 |
+
|
5 |
+
# メニュー等に項目追加
|
6 |
+
def menu_func(self, context):
|
7 |
+
texts = bpy.data.texts
|
8 |
+
text_keys = texts.keys()
|
9 |
+
self.layout.label(text="For CM3D2:", icon_value=common.preview_collections['main']['KISS'].icon_id)
|
10 |
+
row = self.layout.row(align=True)
|
11 |
+
if 'BoneData' in text_keys:
|
12 |
+
txt = bpy.data.texts['BoneData']
|
13 |
+
line_count = 0
|
14 |
+
for line in txt.as_string().split('\n'):
|
15 |
+
if line:
|
16 |
+
line_count += 1
|
17 |
+
row.operator('text.show_text', icon='ARMATURE_DATA', text="BoneData (%d)" % line_count).name = 'BoneData'
|
18 |
+
if 'LocalBoneData' in text_keys:
|
19 |
+
txt = bpy.data.texts['LocalBoneData']
|
20 |
+
line_count = 0
|
21 |
+
for line in txt.as_string().split('\n'):
|
22 |
+
if line:
|
23 |
+
line_count += 1
|
24 |
+
row.operator('text.show_text', icon='BONE_DATA', text="LocalBoneData (%d)" % line_count).name = 'LocalBoneData'
|
25 |
+
if 'BoneData' in text_keys and 'LocalBoneData' in text_keys:
|
26 |
+
if 'BoneData' in texts:
|
27 |
+
if 'BaseBone' not in texts['BoneData']:
|
28 |
+
texts['BoneData']['BaseBone'] = ""
|
29 |
+
row.prop(texts['BoneData'], '["BaseBone"]', text="")
|
30 |
+
row.operator('text.copy_text_bone_data', icon='COPYDOWN', text="")
|
31 |
+
row.operator('text.paste_text_bone_data', icon='PASTEDOWN', text="")
|
32 |
+
if 'Material:0' in text_keys:
|
33 |
+
self.layout.label(text="", icon='MATERIAL_DATA')
|
34 |
+
row = self.layout.row(align=True)
|
35 |
+
pass_count = 0
|
36 |
+
for i in range(99):
|
37 |
+
name = "Material:" + str(i)
|
38 |
+
if name in text_keys:
|
39 |
+
sub_row = row.row(align=True)
|
40 |
+
sub_row.scale_x = 0.5
|
41 |
+
sub_row.operator('text.show_text', text=str(i)).name = name
|
42 |
+
else:
|
43 |
+
pass_count += 1
|
44 |
+
if 9 < pass_count:
|
45 |
+
break
|
46 |
+
if "Material:0" in text_keys:
|
47 |
+
row.operator('text.remove_all_material_texts', icon='X', text="")
|
48 |
+
|
49 |
+
class show_text(bpy.types.Operator):
|
50 |
+
bl_idname = 'text.show_text'
|
51 |
+
bl_label = "Display Text"
|
52 |
+
bl_description = "Displays the specified text in this area"
|
53 |
+
bl_options = {'REGISTER', 'UNDO'}
|
54 |
+
|
55 |
+
name = bpy.props.StringProperty(name="Text name")
|
56 |
+
|
57 |
+
@classmethod
|
58 |
+
def poll(cls, context):
|
59 |
+
if 'text' in dir(context.space_data):
|
60 |
+
return True
|
61 |
+
return False
|
62 |
+
|
63 |
+
def execute(self, context):
|
64 |
+
context.space_data.text = bpy.data.texts[self.name]
|
65 |
+
return {'FINISHED'}
|
66 |
+
|
67 |
+
class copy_text_bone_data(bpy.types.Operator):
|
68 |
+
bl_idname = 'text.copy_text_bone_data'
|
69 |
+
bl_label = "Copy the Bone Data in the text"
|
70 |
+
bl_description = "Bone data is copied to clipboard so it can be pasted in the custom properties."
|
71 |
+
bl_options = {'REGISTER', 'UNDO'}
|
72 |
+
|
73 |
+
@classmethod
|
74 |
+
def poll(cls, context):
|
75 |
+
texts = context.blend_data.texts
|
76 |
+
return 'BoneData' in texts and 'LocalBoneData' in texts
|
77 |
+
|
78 |
+
def execute(self, context):
|
79 |
+
output_text = ""
|
80 |
+
if 'BaseBone' in context.blend_data.texts['BoneData']:
|
81 |
+
output_text += "BaseBone:" + context.blend_data.texts['BoneData']['BaseBone'] + "\n"
|
82 |
+
for line in context.blend_data.texts['BoneData'].as_string().split('\n'):
|
83 |
+
if not line:
|
84 |
+
continue
|
85 |
+
output_text += "BoneData:" + line + "\n"
|
86 |
+
for line in context.blend_data.texts['LocalBoneData'].as_string().split('\n'):
|
87 |
+
if not line:
|
88 |
+
continue
|
89 |
+
output_text += "LocalBoneData:" + line + "\n"
|
90 |
+
context.window_manager.clipboard = output_text
|
91 |
+
self.report(type={'INFO'}, message="Bonedata was copied, mission accomplished")
|
92 |
+
return {'FINISHED'}
|
93 |
+
|
94 |
+
class paste_text_bone_data(bpy.types.Operator):
|
95 |
+
bl_idname = 'text.paste_text_bone_data'
|
96 |
+
bl_label = "Paste Bone Data"
|
97 |
+
bl_description = "Paste Bone Data from clipboard into text editor."
|
98 |
+
bl_options = {'REGISTER', 'UNDO'}
|
99 |
+
|
100 |
+
@classmethod
|
101 |
+
def poll(cls, context):
|
102 |
+
clipboard = context.window_manager.clipboard
|
103 |
+
return 'BoneData:' in clipboard and 'LocalBoneData:' in clipboard
|
104 |
+
|
105 |
+
def execute(self, context):
|
106 |
+
import re
|
107 |
+
clipboard = context.window_manager.clipboard
|
108 |
+
if "BoneData" in context.blend_data.texts:
|
109 |
+
bone_data_text = context.blend_data.texts["BoneData"]
|
110 |
+
bone_data_text.clear()
|
111 |
+
else:
|
112 |
+
bone_data_text = context.blend_data.texts.new("BoneData")
|
113 |
+
if "LocalBoneData" in context.blend_data.texts:
|
114 |
+
local_bone_data_text = context.blend_data.texts["LocalBoneData"]
|
115 |
+
local_bone_data_text.clear()
|
116 |
+
else:
|
117 |
+
local_bone_data_text = context.blend_data.texts.new("LocalBoneData")
|
118 |
+
|
119 |
+
for line in context.window_manager.clipboard.split("\n"):
|
120 |
+
r = re.search('^BaseBone:(.+)$', line)
|
121 |
+
if r:
|
122 |
+
bone_data_text['BaseBone'] = r.groups()[0]
|
123 |
+
local_bone_data_text['BaseBone'] = r.groups()[0]
|
124 |
+
r = re.search('^BoneData:(.+)$', line)
|
125 |
+
if r:
|
126 |
+
if line.count(',') == 4:
|
127 |
+
info = r.groups()[0]
|
128 |
+
bone_data_text.write(info + "\n")
|
129 |
+
r = re.search('^LocalBoneData:(.+)$', line)
|
130 |
+
if r:
|
131 |
+
if line.count(',') == 1:
|
132 |
+
info = r.groups()[0]
|
133 |
+
local_bone_data_text.write(info + "\n")
|
134 |
+
bone_data_text.current_line_index = 0
|
135 |
+
local_bone_data_text.current_line_index = 0
|
136 |
+
self.report(type={'INFO'}, message="Bone Data was pasted, mission accomplished.")
|
137 |
+
return {'FINISHED'}
|
138 |
+
|
139 |
+
class remove_all_material_texts(bpy.types.Operator):
|
140 |
+
bl_idname = 'text.remove_all_material_texts'
|
141 |
+
bl_label = "Delete all .mate data"
|
142 |
+
bl_description = "Removes .mate data in the text editor"
|
143 |
+
bl_options = {'REGISTER', 'UNDO'}
|
144 |
+
|
145 |
+
is_keep_used_material = bpy.props.BoolProperty(name="Keep Used Materials", default=True)
|
146 |
+
|
147 |
+
@classmethod
|
148 |
+
def poll(cls, context):
|
149 |
+
return 'Material:0' in context.blend_data.texts
|
150 |
+
|
151 |
+
def invoke(self, context, event):
|
152 |
+
return context.window_manager.invoke_props_dialog(self)
|
153 |
+
|
154 |
+
def draw(self, context):
|
155 |
+
self.layout.prop(self, 'is_keep_used_material')
|
156 |
+
|
157 |
+
def execute(self, context):
|
158 |
+
remove_texts = []
|
159 |
+
pass_count = 0
|
160 |
+
for i in range(9999):
|
161 |
+
name = 'Material:' + str(i)
|
162 |
+
if name in context.blend_data.texts:
|
163 |
+
remove_texts.append(context.blend_data.texts[name])
|
164 |
+
else:
|
165 |
+
pass_count += 1
|
166 |
+
if 10 < pass_count:
|
167 |
+
break
|
168 |
+
if self.is_keep_used_material:
|
169 |
+
ob = context.active_object
|
170 |
+
if ob:
|
171 |
+
remove_texts = remove_texts[len(ob.material_slots):]
|
172 |
+
for txt in remove_texts:
|
173 |
+
context.blend_data.texts.remove(txt)
|
174 |
+
return {'FINISHED'}
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_VIEW3D_MT_edit_mesh_specials.py
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 「3Dビュー」エリア → メッシュ編集モード → 「W」キー
|
2 |
+
import os, re, sys, bpy, time, bmesh, mathutils
|
3 |
+
from . import common
|
4 |
+
|
5 |
+
# メニュー等に項目追加
|
6 |
+
def menu_func(self, context):
|
7 |
+
icon_id = common.preview_collections['main']['KISS'].icon_id
|
8 |
+
self.layout.separator()
|
9 |
+
self.layout.operator('mesh.selected_mesh_vertex_group_blur', icon_value=icon_id)
|
10 |
+
self.layout.separator()
|
11 |
+
self.layout.operator('mesh.selected_face_sort_front', text="The drawing order of the selected surface is set to the forefront(?)", icon_value=icon_id).is_back = False
|
12 |
+
self.layout.operator('mesh.selected_face_sort_front', text="The drawing order of the selected surface is set to the backmost(?)", icon_value=icon_id).is_back = True
|
13 |
+
|
14 |
+
class selected_mesh_sort_front(bpy.types.Operator):
|
15 |
+
bl_idname = 'mesh.selected_face_sort_front'
|
16 |
+
bl_label = "The drawing order of the selected surface is set to the forefront(?)"
|
17 |
+
bl_description = "Rearranges the drawing order of the currently selected face to the front / back"
|
18 |
+
bl_options = {'REGISTER', 'UNDO'}
|
19 |
+
|
20 |
+
is_back = bpy.props.BoolProperty(name="Back")
|
21 |
+
|
22 |
+
@classmethod
|
23 |
+
def poll(cls, context):
|
24 |
+
ob = context.active_object
|
25 |
+
return ob.type == 'MESH'
|
26 |
+
|
27 |
+
def execute(self, context):
|
28 |
+
ob = context.active_object
|
29 |
+
if ob.mode != 'EDIT':
|
30 |
+
self.report(type={'ERROR'}, message="Please Run in Edit mode. Aborting.")
|
31 |
+
return {'CANCELLED'}
|
32 |
+
me = ob.data
|
33 |
+
bm = bmesh.from_edit_mesh(me)
|
34 |
+
|
35 |
+
bm.faces.ensure_lookup_table()
|
36 |
+
|
37 |
+
selected_face_indexs = []
|
38 |
+
other_face_indexs = []
|
39 |
+
for face in bm.faces:
|
40 |
+
if face.select:
|
41 |
+
selected_face_indexs.append(face.index)
|
42 |
+
else:
|
43 |
+
other_face_indexs.append(face.index)
|
44 |
+
|
45 |
+
output_face_indexs = []
|
46 |
+
if not self.is_back:
|
47 |
+
output_face_indexs = other_face_indexs + selected_face_indexs
|
48 |
+
else:
|
49 |
+
output_face_indexs = selected_face_indexs + other_face_indexs
|
50 |
+
|
51 |
+
for for_index, sorted_index in enumerate(output_face_indexs):
|
52 |
+
bm.faces[sorted_index].index = for_index
|
53 |
+
|
54 |
+
bm.faces.sort()
|
55 |
+
bmesh.update_edit_mesh(me)
|
56 |
+
return {'FINISHED'}
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_VIEW3D_MT_pose_apply.py
ADDED
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 「3Dビュー」エリア → ポーズモード → Ctrl+A (ポーズ → 適用)
|
2 |
+
import os, re, sys, bpy, time, bmesh, mathutils
|
3 |
+
from . import common
|
4 |
+
|
5 |
+
# メニュー等に項目追加
|
6 |
+
def menu_func(self, context):
|
7 |
+
self.layout.separator()
|
8 |
+
self.layout.operator('pose.apply_prime_field', icon_value=common.preview_collections['main']['KISS'].icon_id)
|
9 |
+
|
10 |
+
class apply_prime_field(bpy.types.Operator):
|
11 |
+
bl_idname = 'pose.apply_prime_field'
|
12 |
+
bl_label = "Apply Prime Field"
|
13 |
+
bl_description = "A body will be created that makes custom modeling easy with the current pose."
|
14 |
+
bl_options = {'REGISTER', 'UNDO'}
|
15 |
+
|
16 |
+
is_apply_armature_modifier = bpy.props.BoolProperty(name="Apply Armature Modifier", default=True)
|
17 |
+
is_deform_preserve_volume = bpy.props.BoolProperty(name="Conserve Volume", default=True)
|
18 |
+
|
19 |
+
@classmethod
|
20 |
+
def poll(cls, context):
|
21 |
+
ob = context.active_object
|
22 |
+
if ob:
|
23 |
+
if ob.type == 'ARMATURE':
|
24 |
+
return True
|
25 |
+
return False
|
26 |
+
|
27 |
+
def invoke(self, context, event):
|
28 |
+
return context.window_manager.invoke_props_dialog(self)
|
29 |
+
|
30 |
+
def draw(self, context):
|
31 |
+
self.layout.prop(self, 'is_apply_armature_modifier')
|
32 |
+
self.layout.prop(self, 'is_deform_preserve_volume')
|
33 |
+
|
34 |
+
def execute(self, context):
|
35 |
+
ob = context.active_object
|
36 |
+
arm = ob.data
|
37 |
+
pose = ob.pose
|
38 |
+
|
39 |
+
pre_selected_objects = context.selected_objects
|
40 |
+
pre_selected_pose_bones = context.selected_pose_bones
|
41 |
+
pre_mode = ob.mode
|
42 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
43 |
+
bpy.ops.object.select_all(action='DESELECT')
|
44 |
+
ob.select = True
|
45 |
+
|
46 |
+
if self.is_apply_armature_modifier:
|
47 |
+
for o in context.blend_data.objects:
|
48 |
+
if o.type == 'MESH' and len(o.modifiers):
|
49 |
+
is_applies = [False] * 32
|
50 |
+
for i, mod in enumerate(o.modifiers):
|
51 |
+
if mod.type == 'ARMATURE':
|
52 |
+
if mod.object and mod.object.name == ob.name:
|
53 |
+
is_applies[i] = True
|
54 |
+
if self.is_deform_preserve_volume:
|
55 |
+
mod.use_deform_preserve_volume = True
|
56 |
+
if any(is_applies):
|
57 |
+
override = context.copy()
|
58 |
+
override['object'], override['active_object'] = o, o
|
59 |
+
bpy.ops.object.forced_modifier_apply(override, is_applies=is_applies)
|
60 |
+
|
61 |
+
temp_ob = ob.copy()
|
62 |
+
temp_arm = arm.copy()
|
63 |
+
temp_ob.data = temp_arm
|
64 |
+
context.scene.objects.link(temp_ob)
|
65 |
+
|
66 |
+
context.scene.objects.active = temp_ob
|
67 |
+
bpy.ops.object.mode_set(mode='POSE')
|
68 |
+
bpy.ops.pose.select_all(action='SELECT')
|
69 |
+
bpy.ops.pose.transforms_clear()
|
70 |
+
|
71 |
+
context.scene.objects.active = ob
|
72 |
+
bpy.ops.object.mode_set(mode='POSE')
|
73 |
+
bpy.ops.pose.select_all(action='SELECT')
|
74 |
+
bpy.ops.pose.armature_apply()
|
75 |
+
bpy.ops.pose.constraints_clear()
|
76 |
+
|
77 |
+
consts = []
|
78 |
+
for bone in pose.bones:
|
79 |
+
const = bone.constraints.new('COPY_TRANSFORMS')
|
80 |
+
const.target = temp_ob
|
81 |
+
const.subtarget = bone.name
|
82 |
+
consts.append(const)
|
83 |
+
|
84 |
+
for i in range(10):
|
85 |
+
for const in consts:
|
86 |
+
const.mute = bool(i % 2)
|
87 |
+
|
88 |
+
if i % 2:
|
89 |
+
bpy.ops.pose.visual_transform_apply()
|
90 |
+
else:
|
91 |
+
bpy.ops.pose.transforms_clear()
|
92 |
+
|
93 |
+
for bone in pose.bones:
|
94 |
+
bone.keyframe_insert(data_path='location', frame=i)
|
95 |
+
bone.keyframe_insert(data_path='rotation_quaternion', frame=i)
|
96 |
+
bone.keyframe_insert(data_path='rotation_euler', frame=i)
|
97 |
+
bone.keyframe_insert(data_path='scale', frame=i)
|
98 |
+
bpy.ops.pose.constraints_clear()
|
99 |
+
|
100 |
+
common.remove_data(temp_arm)
|
101 |
+
common.remove_data(temp_ob)
|
102 |
+
|
103 |
+
bpy.ops.pose.select_all(action='DESELECT')
|
104 |
+
for bone in pre_selected_pose_bones:
|
105 |
+
arm.bones[bone.name].select = True
|
106 |
+
|
107 |
+
arm['is T Stance'] = 1
|
108 |
+
|
109 |
+
for o in pre_selected_objects:
|
110 |
+
o.select = True
|
111 |
+
context.scene.objects.active = ob
|
112 |
+
bpy.ops.object.mode_set(mode=pre_mode)
|
113 |
+
return {'FINISHED'}
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_VIEW3D_PT_tools_mesh_shapekey.py
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 「3Dビュー」エリア → 「メッシュエディット」モード → ツールシェルフ → 「シェイプキーツール」パネル
|
2 |
+
import os, re, sys, bpy, time, bmesh, mathutils
|
3 |
+
from . import common
|
4 |
+
|
5 |
+
"""
|
6 |
+
class VIEW3D_PT_tools_mesh_shapekey(bpy.types.Panel):
|
7 |
+
bl_label = "Shape key tool"
|
8 |
+
bl_idname = 'VIEW3D_PT_tools_mesh_shapekey'
|
9 |
+
bl_region_type = 'TOOLS'
|
10 |
+
bl_space_type = 'VIEW_3D'
|
11 |
+
bl_category = 'Tools'
|
12 |
+
bl_context = 'mesh_edit'
|
13 |
+
|
14 |
+
def draw(self, context):
|
15 |
+
pass
|
16 |
+
"""
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/misc_VIEW3D_PT_tools_weightpaint.py
ADDED
@@ -0,0 +1,394 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 「3Dビュー」エリア → 「ウェイトペイント」モード → ツールシェルフ → 「ウェイトツール」パネル
|
2 |
+
import os, re, sys, bpy, time, bmesh, mathutils
|
3 |
+
from . import common
|
4 |
+
|
5 |
+
# メニュー等に項目追加
|
6 |
+
def menu_func(self, context):
|
7 |
+
icon_id = common.preview_collections['main']['KISS'].icon_id
|
8 |
+
box = self.layout.box()
|
9 |
+
column = box.column(align=False)
|
10 |
+
column.prop(context.active_object.data, 'use_paint_mask_vertex', icon='VERTEXSEL', text="Vertex selection mode")
|
11 |
+
column.operator('mesh.selected_mesh_vertex_group_blur', text="Blur the selected part", icon_value=icon_id)
|
12 |
+
column.operator('mesh.selected_mesh_vertex_group_calculation', text="Four arithmetic operation", icon_value=icon_id)
|
13 |
+
|
14 |
+
class selected_mesh_vertex_group_blur(bpy.types.Operator):
|
15 |
+
bl_idname = 'mesh.selected_mesh_vertex_group_blur'
|
16 |
+
bl_label = "Blur the vertex group of the selected part"
|
17 |
+
bl_description = "Blurs the vertex groups of the selected parts."
|
18 |
+
bl_options = {'REGISTER', 'UNDO'}
|
19 |
+
|
20 |
+
items = [
|
21 |
+
('LINER', "Linear", "", 'LINCURVE', 1),
|
22 |
+
('TRIGONOMETRIC', "Trigonometric", "", 'SMOOTHCURVE', 2),
|
23 |
+
]
|
24 |
+
smooth_method = bpy.props.EnumProperty(items=items, name="Damping type", default='TRIGONOMETRIC')
|
25 |
+
|
26 |
+
selection_blur_range_multi = bpy.props.FloatProperty(name="Blur Range", default=4.0, min=0.0, max=100.0, soft_min=0.0, soft_max=100.0, step=50, precision=1)
|
27 |
+
selection_blur_accuracy = bpy.props.IntProperty(name="Blur Accuracy", default=3, min=0, max=10, soft_min=1, soft_max=10)
|
28 |
+
|
29 |
+
items = [
|
30 |
+
('ALL', "All", "", 'COLLAPSEMENU', 1),
|
31 |
+
('ACTIVE', "Active", "", 'LAYER_ACTIVE', 2),
|
32 |
+
]
|
33 |
+
target_vertex_group = bpy.props.EnumProperty(items=items, name="Target vertex groups", default='ALL')
|
34 |
+
items = [
|
35 |
+
('NORMAL', "Normal", "", 'BRUSH_BLUR', 1),
|
36 |
+
('ADD', "Add", "", 'BRUSH_DARKEN', 2),
|
37 |
+
('SUB', "Sub", "", 'BRUSH_LIGHTEN', 3),
|
38 |
+
]
|
39 |
+
blur_mode = bpy.props.EnumProperty(items=items, name="Blur Mode", default='NORMAL')
|
40 |
+
blur_range_multi = bpy.props.FloatProperty(name="Blur Range", default=4.0, min=0.0, max=100.0, soft_min=0.0, soft_max=100.0, step=50, precision=1)
|
41 |
+
blur_count = bpy.props.IntProperty(name="Blur Amount", default=1, min=1, max=100, soft_min=1, soft_max=100)
|
42 |
+
is_vertex_group_limit_total = bpy.props.BoolProperty(name="Limit total weights.", default=True)
|
43 |
+
|
44 |
+
@classmethod
|
45 |
+
def poll(cls, context):
|
46 |
+
ob = context.active_object
|
47 |
+
if ob.type == 'MESH':
|
48 |
+
if len(ob.vertex_groups):
|
49 |
+
return len(ob.data.vertices) and len(ob.data.edges)
|
50 |
+
return False
|
51 |
+
|
52 |
+
def invoke(self, context, event):
|
53 |
+
return context.window_manager.invoke_props_dialog(self)
|
54 |
+
|
55 |
+
def draw(self, context):
|
56 |
+
self.layout.prop(self, 'smooth_method')
|
57 |
+
|
58 |
+
self.layout.label(text="Blur Selected", icon='UV_SYNC_SELECT')
|
59 |
+
self.layout.prop(self, 'selection_blur_range_multi', text="Range | Average of side lengths ×")
|
60 |
+
self.layout.prop(self, 'selection_blur_accuracy', text="Accuracy (number of steps)")
|
61 |
+
|
62 |
+
self.layout.label(text="Blur Vertex Group", icon='GROUP_VERTEX')
|
63 |
+
self.layout.prop(self, 'target_vertex_group', text="Target Group")
|
64 |
+
self.layout.prop(self, 'blur_mode', text="Mode")
|
65 |
+
self.layout.prop(self, 'blur_range_multi', text="Range | Average of side lengths ×")
|
66 |
+
self.layout.prop(self, 'blur_count', text="Blur Count")
|
67 |
+
self.layout.prop(self, 'is_vertex_group_limit_total', icon='IMGDISPLAY')
|
68 |
+
|
69 |
+
def execute(self, context):
|
70 |
+
class EmptyClass: pass
|
71 |
+
|
72 |
+
ob = context.active_object
|
73 |
+
me = ob.data
|
74 |
+
|
75 |
+
pre_mode = ob.mode
|
76 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
77 |
+
|
78 |
+
pre_selected_objects = context.selected_objects[:]
|
79 |
+
for selected_object in pre_selected_objects:
|
80 |
+
selected_object.select = False
|
81 |
+
ob.select = True
|
82 |
+
|
83 |
+
bpy.ops.object.duplicate(linked=False, mode='TRANSLATION')
|
84 |
+
|
85 |
+
selection_ob = context.active_object
|
86 |
+
selection_me = selection_ob.data
|
87 |
+
|
88 |
+
for v in selection_me.vertices:
|
89 |
+
if v.hide: v.hide, v.select = False, False
|
90 |
+
for e in selection_me.edges:
|
91 |
+
if e.hide: e.hide, e.select = False, False
|
92 |
+
for p in selection_me.polygons:
|
93 |
+
if p.hide: p.hide, p.select = False, False
|
94 |
+
|
95 |
+
bpy.ops.object.mode_set(mode='EDIT')
|
96 |
+
bpy.ops.mesh.select_all(action='INVERT')
|
97 |
+
if context.tool_settings.mesh_select_mode[0]:
|
98 |
+
bpy.ops.mesh.delete(type='VERT')
|
99 |
+
elif context.tool_settings.mesh_select_mode[1]:
|
100 |
+
bpy.ops.mesh.delete(type='EDGE')
|
101 |
+
elif context.tool_settings.mesh_select_mode[2]:
|
102 |
+
bpy.ops.mesh.delete(type='FACE')
|
103 |
+
bpy.ops.mesh.select_all(action='SELECT')
|
104 |
+
if 1 <= self.selection_blur_accuracy:
|
105 |
+
bpy.ops.mesh.subdivide(number_cuts=self.selection_blur_accuracy, smoothness=0, quadtri=False, quadcorner='INNERVERT', fractal=0, fractal_along_normal=0, seed=0)
|
106 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
107 |
+
|
108 |
+
selection_kd = mathutils.kdtree.KDTree(len(selection_me.vertices))
|
109 |
+
[selection_kd.insert(v.co, v.index) for v in selection_me.vertices]
|
110 |
+
selection_kd.balance()
|
111 |
+
common.remove_data([selection_ob, selection_me])
|
112 |
+
|
113 |
+
ob.select = True
|
114 |
+
context.scene.objects.active = ob
|
115 |
+
|
116 |
+
bm = bmesh.new()
|
117 |
+
bm.from_mesh(me)
|
118 |
+
edge_lengths = [e.calc_length() for e in bm.edges]
|
119 |
+
bm.free()
|
120 |
+
edge_lengths.sort()
|
121 |
+
edge_lengths_center_index = int( (len(edge_lengths) - 1) * 0.5 )
|
122 |
+
average_edge_length = edge_lengths[edge_lengths_center_index]
|
123 |
+
selection_blur_range = average_edge_length * self.selection_blur_range_multi
|
124 |
+
|
125 |
+
vert_selection_values = [None for v in me.vertices]
|
126 |
+
for vert in me.vertices:
|
127 |
+
co, index, dist = selection_kd.find(vert.co)
|
128 |
+
if dist <= selection_blur_range + 0.00001:
|
129 |
+
if 0 < selection_blur_range:
|
130 |
+
if self.smooth_method == 'TRIGONOMETRIC':
|
131 |
+
value = common.trigonometric_smooth(1.0 - (dist / selection_blur_range))
|
132 |
+
else:
|
133 |
+
value = 1.0 - (dist / selection_blur_range)
|
134 |
+
vert_selection_values[vert.index] = value
|
135 |
+
else:
|
136 |
+
vert_selection_values[vert.index] = 1.0
|
137 |
+
|
138 |
+
"""
|
139 |
+
# 頂点カラーで選択状態を確認
|
140 |
+
preview_vertex_color = me.vertex_colors.new()
|
141 |
+
for loop in me.loops:
|
142 |
+
v = vert_selection_values[loop.vertex_index]
|
143 |
+
if v != None:
|
144 |
+
preview_vertex_color.data[loop.index].color = (v, v, v)
|
145 |
+
else:
|
146 |
+
preview_vertex_color.data[loop.index].color = (0, 0, 0)
|
147 |
+
"""
|
148 |
+
|
149 |
+
kd = mathutils.kdtree.KDTree(len(me.vertices))
|
150 |
+
[kd.insert(v.co, v.index) for v in me.vertices]
|
151 |
+
kd.balance()
|
152 |
+
|
153 |
+
blur_range = average_edge_length * self.blur_range_multi
|
154 |
+
|
155 |
+
for i in range(self.blur_count):
|
156 |
+
|
157 |
+
pre_vert_weights = [[0.0 for vg in ob.vertex_groups] for v in me.vertices]
|
158 |
+
for vert in me.vertices:
|
159 |
+
for vge in vert.groups:
|
160 |
+
pre_vert_weights[vert.index][vge.group] = vge.weight
|
161 |
+
|
162 |
+
for vert in me.vertices:
|
163 |
+
selection_value = vert_selection_values[vert.index]
|
164 |
+
if selection_value == None: continue
|
165 |
+
|
166 |
+
near_infos = []
|
167 |
+
total_effect = 0.0
|
168 |
+
for co, index, dist in kd.find_range(vert.co, blur_range):
|
169 |
+
ec = EmptyClass()
|
170 |
+
ec.index = index
|
171 |
+
if 0 < blur_range:
|
172 |
+
raw_effect = 1.0 - (dist / blur_range)
|
173 |
+
if self.smooth_method == 'TRIGONOMETRIC': ec.effect = common.trigonometric_smooth(raw_effect)
|
174 |
+
else: ec.effect = raw_effect
|
175 |
+
else:
|
176 |
+
ec.effect = 1.0
|
177 |
+
total_effect += ec.effect
|
178 |
+
near_infos.append(ec)
|
179 |
+
|
180 |
+
new_vert_weight = [0.0 for vg in ob.vertex_groups]
|
181 |
+
for ec in near_infos:
|
182 |
+
pre_vert_weight = pre_vert_weights[ec.index]
|
183 |
+
weight_multi = ec.effect / total_effect
|
184 |
+
for vg_index, near_vert_pre_weight_value in enumerate(pre_vert_weight):
|
185 |
+
current_vert_pre_weight_value = pre_vert_weights[vert.index][vg_index]
|
186 |
+
|
187 |
+
if self.blur_mode == 'NORMAL':
|
188 |
+
send_weight_value = near_vert_pre_weight_value
|
189 |
+
elif self.blur_mode == 'ADD':
|
190 |
+
if current_vert_pre_weight_value < near_vert_pre_weight_value:
|
191 |
+
send_weight_value = near_vert_pre_weight_value
|
192 |
+
else:
|
193 |
+
send_weight_value = current_vert_pre_weight_value
|
194 |
+
elif self.blur_mode == 'SUB':
|
195 |
+
if near_vert_pre_weight_value < current_vert_pre_weight_value:
|
196 |
+
send_weight_value = near_vert_pre_weight_value
|
197 |
+
else:
|
198 |
+
send_weight_value = current_vert_pre_weight_value
|
199 |
+
|
200 |
+
new_vert_weight[vg_index] += send_weight_value * weight_multi
|
201 |
+
|
202 |
+
for vg in ob.vertex_groups:
|
203 |
+
if self.target_vertex_group == 'ACTIVE' and ob.vertex_groups.active.name != vg.name: continue
|
204 |
+
if vg.lock_weight: continue
|
205 |
+
|
206 |
+
pre_weight = pre_vert_weights[vert.index][vg.index]
|
207 |
+
new_weight = new_vert_weight[vg.index]
|
208 |
+
result_weight = (pre_weight * (1.0 - selection_value)) + (new_weight * selection_value)
|
209 |
+
|
210 |
+
if 0.0 < result_weight:
|
211 |
+
vg.add([vert.index], result_weight, 'REPLACE')
|
212 |
+
else:
|
213 |
+
vg.remove([vert.index])
|
214 |
+
|
215 |
+
if self.is_vertex_group_limit_total:
|
216 |
+
bpy.ops.object.vertex_group_limit_total(group_select_mode='ALL', limit=4)
|
217 |
+
|
218 |
+
bpy.ops.object.mode_set(mode=pre_mode)
|
219 |
+
for selected_object in pre_selected_objects:
|
220 |
+
selected_object.select = True
|
221 |
+
return {'FINISHED'}
|
222 |
+
|
223 |
+
class selected_mesh_vertex_group_calculation(bpy.types.Operator):
|
224 |
+
bl_idname = 'mesh.selected_mesh_vertex_group_calculation'
|
225 |
+
bl_label = "Four arithmetic operations on the vertex groups of the selection"
|
226 |
+
bl_description = "Applies four arithmetic operations to the vertex groups of selection."
|
227 |
+
bl_options = {'REGISTER', 'UNDO'}
|
228 |
+
|
229 |
+
items = [
|
230 |
+
('LINER', "Iinear", "", 'LINCURVE', 1),
|
231 |
+
('TRIGONOMETRIC', "Trigonometric", "", 'SMOOTHCURVE', 2),
|
232 |
+
]
|
233 |
+
smooth_method = bpy.props.EnumProperty(items=items, name="Smooth Methid", default='TRIGONOMETRIC')
|
234 |
+
|
235 |
+
selection_blur_range_multi = bpy.props.FloatProperty(name="Blur Range", default=4.0, min=0.0, max=100.0, soft_min=0.0, soft_max=100.0, step=50, precision=1)
|
236 |
+
selection_blur_accuracy = bpy.props.IntProperty(name="Blur Accuracy", default=3, min=0, max=10, soft_min=1, soft_max=10)
|
237 |
+
|
238 |
+
items = [
|
239 |
+
('ACTIVE', "Active", "", 'LAYER_ACTIVE', 1),
|
240 |
+
]
|
241 |
+
target_vertex_group = bpy.props.EnumProperty(items=items, name="Target Vertex Group", default='ACTIVE')
|
242 |
+
items = [
|
243 |
+
('ADD', "Add", "", 'ZOOMIN', 1),
|
244 |
+
('SUB', "Sub", "", 'ZOOMOUT', 2),
|
245 |
+
('MULTI', "Multi", "", 'X', 3),
|
246 |
+
('DIV', "Div", "", 'FULLSCREEN_EXIT', 4),
|
247 |
+
]
|
248 |
+
calculation_mode = bpy.props.EnumProperty(items=items, name="Arithmetic operation mode", default='ADD')
|
249 |
+
calculation_value = bpy.props.FloatProperty(name="Value", default=1.0, min=-100.0, max=100.0, soft_min=-100.0, soft_max=100.0, step=10, precision=1)
|
250 |
+
|
251 |
+
@classmethod
|
252 |
+
def poll(cls, context):
|
253 |
+
ob = context.active_object
|
254 |
+
if ob.type == 'MESH':
|
255 |
+
return bool(len(ob.vertex_groups))
|
256 |
+
return False
|
257 |
+
|
258 |
+
def draw(self, context):
|
259 |
+
self.layout.label(text="Blur the selection of the vertex groups.", icon='UV_SYNC_SELECT')
|
260 |
+
self.layout.prop(self, 'smooth_method')
|
261 |
+
self.layout.prop(self, 'selection_blur_range_multi', text="Range | Average of side length ×")
|
262 |
+
self.layout.prop(self, 'selection_blur_accuracy', text="Accuracy (Steps)")
|
263 |
+
|
264 |
+
self.layout.label(text="Four arithmetic operations", icon='BRUSH_ADD')
|
265 |
+
self.layout.prop(self, 'target_vertex_group', text="Target Group")
|
266 |
+
self.layout.prop(self, 'calculation_mode', text="Mode")
|
267 |
+
self.layout.prop(self, 'calculation_value', text="Value")
|
268 |
+
|
269 |
+
calculation_text = "Expression: Original weight "
|
270 |
+
if self.calculation_mode == 'ADD': calculation_text += "+"
|
271 |
+
elif self.calculation_mode == 'SUB': calculation_text += "-"
|
272 |
+
elif self.calculation_mode == 'MULTI': calculation_text += "×"
|
273 |
+
elif self.calculation_mode == 'DIV': calculation_text += "÷"
|
274 |
+
calculation_text += " " + str(round(self.calculation_value, 1))
|
275 |
+
self.layout.label(text=calculation_text)
|
276 |
+
|
277 |
+
def execute(self, context):
|
278 |
+
class EmptyClass: pass
|
279 |
+
|
280 |
+
if self.calculation_mode == 'DIV' and self.calculation_value == 0.0:
|
281 |
+
self.report(type={'ERROR'}, message="Cannot divide by zero, Aborting.")
|
282 |
+
return {'CANCELLED'}
|
283 |
+
|
284 |
+
ob = context.active_object
|
285 |
+
me = ob.data
|
286 |
+
|
287 |
+
pre_mode = ob.mode
|
288 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
289 |
+
|
290 |
+
pre_selected_objects = context.selected_objects[:]
|
291 |
+
for selected_object in pre_selected_objects:
|
292 |
+
selected_object.select = False
|
293 |
+
ob.select = True
|
294 |
+
|
295 |
+
bpy.ops.object.duplicate(linked=False, mode='TRANSLATION')
|
296 |
+
|
297 |
+
selection_ob = context.active_object
|
298 |
+
selection_me = selection_ob.data
|
299 |
+
|
300 |
+
for v in selection_me.vertices:
|
301 |
+
if v.hide: v.hide, v.select = False, False
|
302 |
+
for e in selection_me.edges:
|
303 |
+
if e.hide: e.hide, e.select = False, False
|
304 |
+
for p in selection_me.polygons:
|
305 |
+
if p.hide: p.hide, p.select = False, False
|
306 |
+
|
307 |
+
bpy.ops.object.mode_set(mode='EDIT')
|
308 |
+
bpy.ops.mesh.select_all(action='INVERT')
|
309 |
+
if context.tool_settings.mesh_select_mode[0]:
|
310 |
+
bpy.ops.mesh.delete(type='VERT')
|
311 |
+
elif context.tool_settings.mesh_select_mode[1]:
|
312 |
+
bpy.ops.mesh.delete(type='EDGE')
|
313 |
+
elif context.tool_settings.mesh_select_mode[2]:
|
314 |
+
bpy.ops.mesh.delete(type='FACE')
|
315 |
+
bpy.ops.mesh.select_all(action='SELECT')
|
316 |
+
if 1 <= self.selection_blur_accuracy:
|
317 |
+
bpy.ops.mesh.subdivide(number_cuts=self.selection_blur_accuracy, smoothness=0, quadtri=False, quadcorner='INNERVERT', fractal=0, fractal_along_normal=0, seed=0)
|
318 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
319 |
+
|
320 |
+
selection_kd = mathutils.kdtree.KDTree(len(selection_me.vertices))
|
321 |
+
[selection_kd.insert(v.co, v.index) for v in selection_me.vertices]
|
322 |
+
selection_kd.balance()
|
323 |
+
common.remove_data([selection_ob, selection_me])
|
324 |
+
|
325 |
+
ob.select = True
|
326 |
+
context.scene.objects.active = ob
|
327 |
+
|
328 |
+
bm = bmesh.new()
|
329 |
+
bm.from_mesh(me)
|
330 |
+
edge_lengths = [e.calc_length() for e in bm.edges]
|
331 |
+
bm.free()
|
332 |
+
edge_lengths.sort()
|
333 |
+
edge_lengths_center_index = int( (len(edge_lengths) - 1) * 0.5 )
|
334 |
+
average_edge_length = edge_lengths[edge_lengths_center_index]
|
335 |
+
selection_blur_range = average_edge_length * self.selection_blur_range_multi
|
336 |
+
|
337 |
+
vert_selection_values = [None for v in me.vertices]
|
338 |
+
for vert in me.vertices:
|
339 |
+
co, index, dist = selection_kd.find(vert.co)
|
340 |
+
if dist <= selection_blur_range + 0.00001:
|
341 |
+
if 0 < selection_blur_range:
|
342 |
+
if self.smooth_method == 'TRIGONOMETRIC':
|
343 |
+
value = common.trigonometric_smooth(1.0 - (dist / selection_blur_range))
|
344 |
+
else:
|
345 |
+
value = 1.0 - (dist / selection_blur_range)
|
346 |
+
vert_selection_values[vert.index] = value
|
347 |
+
else:
|
348 |
+
vert_selection_values[vert.index] = 1.0
|
349 |
+
|
350 |
+
"""
|
351 |
+
# 頂点カラーで選択状態を確認
|
352 |
+
preview_vertex_color = me.vertex_colors.new()
|
353 |
+
for loop in me.loops:
|
354 |
+
v = vert_selection_values[loop.vertex_index]
|
355 |
+
if v != None:
|
356 |
+
preview_vertex_color.data[loop.index].color = (v, v, v)
|
357 |
+
else:
|
358 |
+
preview_vertex_color.data[loop.index].color = (0, 0, 0)
|
359 |
+
"""
|
360 |
+
|
361 |
+
for vert in me.vertices:
|
362 |
+
effect = vert_selection_values[vert.index]
|
363 |
+
if effect == None: continue
|
364 |
+
|
365 |
+
pre_vert_weight = 0.0
|
366 |
+
for vge in vert.groups:
|
367 |
+
if ob.vertex_groups.active.index == vge.group:
|
368 |
+
pre_vert_weight = vge.weight
|
369 |
+
|
370 |
+
if self.calculation_mode == 'ADD':
|
371 |
+
new_vert_weight = pre_vert_weight + self.calculation_value
|
372 |
+
elif self.calculation_mode == 'SUB':
|
373 |
+
new_vert_weight = pre_vert_weight - self.calculation_value
|
374 |
+
elif self.calculation_mode == 'MULTI':
|
375 |
+
new_vert_weight = pre_vert_weight * self.calculation_value
|
376 |
+
elif self.calculation_mode == 'DIV':
|
377 |
+
new_vert_weight = pre_vert_weight / self.calculation_value
|
378 |
+
|
379 |
+
if new_vert_weight < 0.0:
|
380 |
+
new_vert_weight = 0.0
|
381 |
+
elif 1.0 < new_vert_weight:
|
382 |
+
new_vert_weight = 1.0
|
383 |
+
|
384 |
+
new_vert_weight = (pre_vert_weight * (1.0 - effect)) + (new_vert_weight * effect)
|
385 |
+
|
386 |
+
if 0.0 < new_vert_weight:
|
387 |
+
ob.vertex_groups.active.add([vert.index], new_vert_weight, 'REPLACE')
|
388 |
+
else:
|
389 |
+
ob.vertex_groups.active.remove([vert.index])
|
390 |
+
|
391 |
+
bpy.ops.object.mode_set(mode=pre_mode)
|
392 |
+
for selected_object in pre_selected_objects:
|
393 |
+
selected_object.select = True
|
394 |
+
return {'FINISHED'}
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/model_export.py
ADDED
@@ -0,0 +1,849 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import bpy, bmesh, mathutils
|
2 |
+
import os, re, struct, time, math
|
3 |
+
from operator import itemgetter
|
4 |
+
from . import common
|
5 |
+
|
6 |
+
|
7 |
+
|
8 |
+
# メインオペレーター
|
9 |
+
class export_cm3d2_model(bpy.types.Operator):
|
10 |
+
bl_idname = 'export_mesh.export_cm3d2_model'
|
11 |
+
bl_label = "CM3D2 Model (.model)"
|
12 |
+
bl_description = "Will export a mesh in CM3D2 .Model Format."
|
13 |
+
bl_options = {'REGISTER'}
|
14 |
+
|
15 |
+
filepath = bpy.props.StringProperty(subtype='FILE_PATH')
|
16 |
+
filename_ext = ".model"
|
17 |
+
filter_glob = bpy.props.StringProperty(default="*.model", options={'HIDDEN'})
|
18 |
+
|
19 |
+
scale = bpy.props.FloatProperty(name="Scale", default=0.2, min=0.01, max=100, soft_min=0.01, soft_max=100, step=10, precision=2, description="How the mesh will be scaled at the time of export.")
|
20 |
+
|
21 |
+
is_backup = bpy.props.BoolProperty(name="Backup", default=True, description="Will backup any previous files with the same name.")
|
22 |
+
|
23 |
+
version = bpy.props.IntProperty(name="Version", default=1000, min=1000, max=1111, soft_min=1000, soft_max=1111, step=1)
|
24 |
+
model_name = bpy.props.StringProperty(name="Model Name", default="*")
|
25 |
+
base_bone_name = bpy.props.StringProperty(name="Base Bone Name", default="*")
|
26 |
+
|
27 |
+
items = [
|
28 |
+
('ARMATURE', "Armature", "", 'OUTLINER_OB_ARMATURE', 1),
|
29 |
+
('TEXT', "Text", "", 'FILE_TEXT', 2),
|
30 |
+
('OBJECT_PROPERTY', "Object Data", "", 'OBJECT_DATAMODE', 3),
|
31 |
+
('ARMATURE_PROPERTY', "Armature Data", "", 'ARMATURE_DATA', 4),
|
32 |
+
]
|
33 |
+
bone_info_mode = bpy.props.EnumProperty(items=items, name="Bone Data Source", default='OBJECT_PROPERTY', description="This will decide from where the Bone Data is gathered from.")
|
34 |
+
|
35 |
+
items = [
|
36 |
+
('TEXT', "Text", "", 'FILE_TEXT', 1),
|
37 |
+
('MATERIAL', "Material", "", 'MATERIAL', 2),
|
38 |
+
]
|
39 |
+
mate_info_mode = bpy.props.EnumProperty(items=items, name="Material Source", default='MATERIAL', description="This will decide from where the Material Data is gathered from.")
|
40 |
+
|
41 |
+
is_arrange_name = bpy.props.BoolProperty(name="Delete Duplicate Name Numbers", default=True, description="This will delete the numbers that are added to duplicate names such as [.001]")
|
42 |
+
|
43 |
+
is_convert_tris = bpy.props.BoolProperty(name="Triangulate", default=True, description="Will triangulate any none triangular faces.")
|
44 |
+
is_normalize_weight = bpy.props.BoolProperty(name="Normalize Weights", default=True, description="Will normalize all Vertex Weights so that the sum of the weights on a single vertex is equal to 1.")
|
45 |
+
is_convert_bone_weight_names = bpy.props.BoolProperty(name="Convert Vertex Groups for CM3D2", default=True, description="This will change the vertex group names to CM3D2's format if it is in Blenders format.")
|
46 |
+
is_apply_modifiers = bpy.props.BoolProperty(name="Apply Modifiers", default=False)
|
47 |
+
custom_normal_blend = bpy.props.FloatProperty(name="CM3D2 Blending Ratio", default=0.5, min=0, max=1, soft_min=0, soft_max=1, step=3, precision=0)
|
48 |
+
|
49 |
+
is_batch = bpy.props.BoolProperty(name="Batch Mode", default=False, description="We do not switch modes or select error locations.[?]")
|
50 |
+
|
51 |
+
@classmethod
|
52 |
+
def poll(cls, context):
|
53 |
+
ob = context.active_object
|
54 |
+
if ob:
|
55 |
+
if ob.type == 'MESH':
|
56 |
+
return True
|
57 |
+
return False
|
58 |
+
|
59 |
+
def report_cancel(self, report_message, report_type={'ERROR'}, resobj={'CANCELLED'}):
|
60 |
+
"""エラーメッセージを出力してキャンセルオブジェクトを返す"""
|
61 |
+
self.report(type=report_type, message=report_message)
|
62 |
+
return resobj
|
63 |
+
|
64 |
+
|
65 |
+
def precheck(self, context):
|
66 |
+
"""データの成否チェック"""
|
67 |
+
ob = context.active_object
|
68 |
+
if not ob:
|
69 |
+
return self.report_cancel("There is no Active Object.")
|
70 |
+
if ob.type != 'MESH':
|
71 |
+
return self.report_cancel("Please select the mesh first.")
|
72 |
+
if not len(ob.material_slots):
|
73 |
+
return self.report_cancel("There is no material.")
|
74 |
+
for slot in ob.material_slots:
|
75 |
+
if not slot.material:
|
76 |
+
return self.report_cancel("Please Delete the empty Material Slot")
|
77 |
+
try:
|
78 |
+
slot.material['shader1']
|
79 |
+
slot.material['shader2']
|
80 |
+
except:
|
81 |
+
return self.report_cancel("Please add [shader 1] and [shader 2] to the material")
|
82 |
+
me = ob.data
|
83 |
+
if not me.uv_layers.active:
|
84 |
+
return self.report_cancel("There is no UV")
|
85 |
+
if 65535 < len(me.vertices):
|
86 |
+
return self.report_cancel("Too many Vertices. Please reduce to at least 65535")
|
87 |
+
return None
|
88 |
+
|
89 |
+
|
90 |
+
def invoke(self, context, event):
|
91 |
+
res = self.precheck(context)
|
92 |
+
if res: return res
|
93 |
+
ob = context.active_object
|
94 |
+
|
95 |
+
# model名とか
|
96 |
+
ob_names = common.remove_serial_number(ob.name, self.is_arrange_name).split('.')
|
97 |
+
self.model_name = ob_names[0]
|
98 |
+
self.base_bone_name = ob_names[1] if 2 <= len(ob_names) else 'Auto'
|
99 |
+
|
100 |
+
# ボーン情報元のデフォルトオプションを取得
|
101 |
+
if "BoneData" in context.blend_data.texts:
|
102 |
+
if "LocalBoneData" in context.blend_data.texts:
|
103 |
+
self.bone_info_mode = 'TEXT'
|
104 |
+
if "BoneData:0" in ob:
|
105 |
+
if "LocalBoneData:0" in ob:
|
106 |
+
self.bone_info_mode = 'OBJECT_PROPERTY'
|
107 |
+
arm_ob = ob.parent
|
108 |
+
if arm_ob:
|
109 |
+
if arm_ob.type == 'ARMATURE':
|
110 |
+
self.bone_info_mode = 'ARMATURE_PROPERTY'
|
111 |
+
else:
|
112 |
+
for mod in ob.modifiers:
|
113 |
+
if mod.type == 'ARMATURE':
|
114 |
+
if mod.object:
|
115 |
+
self.bone_info_mode = 'ARMATURE_PROPERTY'
|
116 |
+
break
|
117 |
+
|
118 |
+
# エクスポート時のデフォルトパスを取得
|
119 |
+
if common.preferences().model_default_path:
|
120 |
+
self.filepath = common.default_cm3d2_dir(common.preferences().model_default_path, self.model_name, "model")
|
121 |
+
else:
|
122 |
+
self.filepath = common.default_cm3d2_dir(common.preferences().model_export_path, self.model_name, "model")
|
123 |
+
|
124 |
+
# バックアップ関係
|
125 |
+
self.is_backup = bool(common.preferences().backup_ext)
|
126 |
+
|
127 |
+
self.scale = 1.0 / common.preferences().scale
|
128 |
+
context.window_manager.fileselect_add(self)
|
129 |
+
return {'RUNNING_MODAL'}
|
130 |
+
|
131 |
+
|
132 |
+
# 'is_batch' がオンなら非表示
|
133 |
+
def draw(self, context):
|
134 |
+
self.layout.prop(self, 'scale')
|
135 |
+
row = self.layout.row()
|
136 |
+
row.prop(self, 'is_backup', icon='FILE_BACKUP')
|
137 |
+
if not common.preferences().backup_ext:
|
138 |
+
row.enabled = False
|
139 |
+
self.layout.prop(self, 'is_arrange_name', icon='SAVE_AS')
|
140 |
+
box = self.layout.box()
|
141 |
+
box.prop(self, 'version', icon='LINENUMBERS_ON')
|
142 |
+
box.prop(self, 'model_name', icon='SORTALPHA')
|
143 |
+
|
144 |
+
row = box.row()
|
145 |
+
row.prop(self, 'base_bone_name', icon='CONSTRAINT_BONE')
|
146 |
+
if self.base_bone_name == 'Auto':
|
147 |
+
row.enabled = False
|
148 |
+
|
149 |
+
box = self.layout.box()
|
150 |
+
col = box.column(align=True)
|
151 |
+
col.label(text="Bone Data Source", icon='BONE_DATA')
|
152 |
+
col.prop(self, 'bone_info_mode', icon='BONE_DATA', expand=True)
|
153 |
+
col = box.column(align=True)
|
154 |
+
col.label(text="Material Source", icon='MATERIAL')
|
155 |
+
col.prop(self, 'mate_info_mode', icon='MATERIAL', expand=True)
|
156 |
+
box = self.layout.box()
|
157 |
+
box.label("Triangulate")
|
158 |
+
box.prop(self, 'is_convert_tris', icon='MESH_DATA')
|
159 |
+
sub_box = box.box()
|
160 |
+
sub_box.prop(self, 'is_normalize_weight', icon='MOD_VERTEX_WEIGHT')
|
161 |
+
sub_box.prop(self, 'is_convert_bone_weight_names', icon_value=common.preview_collections['main']['KISS'].icon_id)
|
162 |
+
sub_box = box.box()
|
163 |
+
sub_box.prop(self, 'is_apply_modifiers', icon='MODIFIER')
|
164 |
+
row = sub_box.row()
|
165 |
+
row.prop(self, 'custom_normal_blend', icon='SNAP_NORMAL', slider=True)
|
166 |
+
row.enabled = self.is_apply_modifiers
|
167 |
+
|
168 |
+
|
169 |
+
def execute(self, context):
|
170 |
+
"""モデルファイルを出力"""
|
171 |
+
start_time = time.time()
|
172 |
+
|
173 |
+
if not self.is_batch:
|
174 |
+
common.preferences().model_export_path = self.filepath
|
175 |
+
common.preferences().scale = 1.0 / self.scale
|
176 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
177 |
+
|
178 |
+
|
179 |
+
context.window_manager.progress_begin(0, 10)
|
180 |
+
context.window_manager.progress_update(0)
|
181 |
+
|
182 |
+
res = self.precheck(context)
|
183 |
+
if res: return res
|
184 |
+
|
185 |
+
ob = context.active_object
|
186 |
+
me = ob.data
|
187 |
+
|
188 |
+
if ob.active_shape_key_index != 0:
|
189 |
+
ob.active_shape_key_index = 0
|
190 |
+
me.update()
|
191 |
+
|
192 |
+
# モディファイアを適用する場合
|
193 |
+
if self.is_apply_modifiers:
|
194 |
+
new_ob = ob.copy()
|
195 |
+
new_me = ob.data.copy()
|
196 |
+
new_ob.data = new_me
|
197 |
+
context.scene.objects.link(new_ob)
|
198 |
+
context.scene.objects.active = new_ob
|
199 |
+
new_ob.select = True
|
200 |
+
|
201 |
+
source_ob = ob
|
202 |
+
source_me = me
|
203 |
+
ob = new_ob
|
204 |
+
me = new_me
|
205 |
+
|
206 |
+
bpy.ops.object.forced_modifier_apply(is_applies=[True for i in range(32)], custom_normal_blend=self.custom_normal_blend)
|
207 |
+
|
208 |
+
# データの成否チェック
|
209 |
+
if self.bone_info_mode == 'ARMATURE':
|
210 |
+
arm_ob = ob.parent
|
211 |
+
if arm_ob and arm_ob.type != 'ARMATURE':
|
212 |
+
return self.report_cancel("The parent of the mesh object is not an armature")
|
213 |
+
if not arm_ob:
|
214 |
+
try:
|
215 |
+
arm_ob = next(mod for mod in ob.modifiers if mod.type == 'ARMATURE' and mod.object)
|
216 |
+
except StopIteration:
|
217 |
+
return self.report_cancel("Armature not found, please make it parent or modifier")
|
218 |
+
arm_ob = arm_ob.object
|
219 |
+
elif self.bone_info_mode == 'TEXT':
|
220 |
+
if "BoneData" not in context.blend_data.texts:
|
221 |
+
return self.report_cancel("BoneData in Text cannot be found. Aborting.")
|
222 |
+
if "LocalBoneData" not in context.blend_data.texts:
|
223 |
+
return self.report_cancel("Text 'LocalBoneData' can not be found, aborted")
|
224 |
+
elif self.bone_info_mode == 'OBJECT_PROPERTY':
|
225 |
+
if "BoneData:0" not in ob:
|
226 |
+
return self.report_cancel("There is no BoneData in the custom properties of the object")
|
227 |
+
if "LocalBoneData:0" not in ob:
|
228 |
+
return self.report_cancel("There is no LocalBoneData in the custom properties of the object")
|
229 |
+
elif self.bone_info_mode == 'ARMATURE_PROPERTY':
|
230 |
+
arm_ob = ob.parent
|
231 |
+
if arm_ob and arm_ob.type != 'ARMATURE':
|
232 |
+
return self.report_cancel("The Parent of the Mesh object is not an Armature")
|
233 |
+
if not arm_ob:
|
234 |
+
try:
|
235 |
+
arm_ob = next(mod for mod in ob.modifiers if mod.type == 'ARMATURE' and mod.object)
|
236 |
+
except StopIteration:
|
237 |
+
return self.report_cancel("Armature not found. Please parent the Armature to the mesh or add an Armature modifier with the corresponding Armature.")
|
238 |
+
arm_ob = arm_ob.object
|
239 |
+
if "BoneData:0" not in arm_ob.data:
|
240 |
+
return self.report_cancel("There is no BoneData in the custom properties of the Armature.")
|
241 |
+
if "LocalBoneData:0" not in arm_ob.data:
|
242 |
+
return self.report_cancel("There is no Local BoneData in the custom properties of the Armature.")
|
243 |
+
else:
|
244 |
+
return self.report_cancel("The bone Information source is not working? Please choose another.")
|
245 |
+
|
246 |
+
if self.mate_info_mode == 'TEXT':
|
247 |
+
for index, slot in enumerate(ob.material_slots):
|
248 |
+
if "Material:" + str(index) not in context.blend_data.texts:
|
249 |
+
return self.report_cancel("Material Data from Text is incomplete.")
|
250 |
+
context.window_manager.progress_update(1)
|
251 |
+
|
252 |
+
# model名とか
|
253 |
+
ob_names = common.remove_serial_number(ob.name, self.is_arrange_name).split('.')
|
254 |
+
if self.model_name == '*':
|
255 |
+
self.model_name = ob_names[0]
|
256 |
+
if self.base_bone_name == '*':
|
257 |
+
self.base_bone_name = ob_names[1] if 2 <= len(ob_names) else 'Auto'
|
258 |
+
|
259 |
+
# BoneData情報読み込み
|
260 |
+
base_bone_candidate = None
|
261 |
+
bone_data = []
|
262 |
+
if self.bone_info_mode == 'ARMATURE':
|
263 |
+
bone_data = self.armature_bone_data_parser(arm_ob)
|
264 |
+
base_bone_candidate = arm_ob.data['BaseBone']
|
265 |
+
elif self.bone_info_mode == 'TEXT':
|
266 |
+
bone_data_text = context.blend_data.texts["BoneData"]
|
267 |
+
if 'BaseBone' in bone_data_text:
|
268 |
+
base_bone_candidate = bone_data_text['BaseBone']
|
269 |
+
bone_data = self.bone_data_parser(l.body for l in bone_data_text.lines)
|
270 |
+
elif self.bone_info_mode in ['OBJECT_PROPERTY', 'ARMATURE_PROPERTY']:
|
271 |
+
target = ob if self.bone_info_mode == 'OBJECT_PROPERTY' else arm_ob.data
|
272 |
+
if 'BaseBone' in target:
|
273 |
+
base_bone_candidate = target['BaseBone']
|
274 |
+
bone_data = self.bone_data_parser(self.indexed_data_generator(target, prefix='BoneData:'))
|
275 |
+
if len(bone_data) <= 0:
|
276 |
+
return self.report_cancel("There is no such Base Bone in the Bone Data")
|
277 |
+
|
278 |
+
if self.base_bone_name not in (b['name'] for b in bone_data):
|
279 |
+
if base_bone_candidate and self.base_bone_name == 'Auto':
|
280 |
+
self.base_bone_name = base_bone_candidate
|
281 |
+
else:
|
282 |
+
return self.report_cancel("The second half of the Object name should be the bone names(?)")
|
283 |
+
context.window_manager.progress_update(2)
|
284 |
+
|
285 |
+
# LocalBoneData情報読み込み
|
286 |
+
local_bone_data = []
|
287 |
+
if self.bone_info_mode == 'ARMATURE':
|
288 |
+
local_bone_data = self.armature_local_bone_data_parser(arm_ob)
|
289 |
+
elif self.bone_info_mode == 'TEXT':
|
290 |
+
local_bone_data_text = context.blend_data.texts["LocalBoneData"]
|
291 |
+
local_bone_data = self.local_bone_data_parser(l.body for l in local_bone_data_text.lines)
|
292 |
+
elif self.bone_info_mode in ['OBJECT_PROPERTY', 'ARMATURE_PROPERTY']:
|
293 |
+
target = ob if self.bone_info_mode == 'OBJECT_PROPERTY' else arm_ob.data
|
294 |
+
local_bone_data = self.local_bone_data_parser(self.indexed_data_generator(target, prefix='LocalBoneData:'))
|
295 |
+
if len(local_bone_data) <= 0:
|
296 |
+
return self.report_cancel("LocalBoneData in text does not contain valid data")
|
297 |
+
local_bone_name_indices = {bone['name']:index for index, bone in enumerate(local_bone_data)}
|
298 |
+
context.window_manager.progress_update(3)
|
299 |
+
|
300 |
+
# ウェイト情報読み込み
|
301 |
+
vertices = []
|
302 |
+
is_over_one = 0
|
303 |
+
is_under_one = 0
|
304 |
+
for i, vert in enumerate(me.vertices):
|
305 |
+
vgs = []
|
306 |
+
for vg in vert.groups:
|
307 |
+
name = common.encode_bone_name(ob.vertex_groups[vg.group].name, self.is_convert_bone_weight_names)
|
308 |
+
index = local_bone_name_indices.get(name, -1)
|
309 |
+
if 0 <= index and 0.0 < vg.weight:
|
310 |
+
vgs.append([index, vg.weight])
|
311 |
+
if len(vgs) == 0:
|
312 |
+
if not self.is_batch:
|
313 |
+
self.select_no_weight_vertices(context, local_bone_name_indices)
|
314 |
+
return self.report_cancel("A Vertex with no Weight assigned has been found. Aborting.")
|
315 |
+
vgs = sorted(vgs, key=itemgetter(1), reverse=True)[0:4]
|
316 |
+
total = sum(vg[1] for vg in vgs)
|
317 |
+
if self.is_normalize_weight:
|
318 |
+
for vg in vgs:
|
319 |
+
vg[1] /= total
|
320 |
+
else:
|
321 |
+
if 1.01 < total:
|
322 |
+
is_over_one += 1
|
323 |
+
elif total < 0.99:
|
324 |
+
is_under_one += 1
|
325 |
+
if len(vgs) < 4:
|
326 |
+
vgs += [(0, 0.0)] * (4 - len(vgs))
|
327 |
+
vertices.append({
|
328 |
+
'index': vert.index,
|
329 |
+
'face_indexs': list(map(itemgetter(0), vgs)),
|
330 |
+
'weights': list(map(itemgetter(1), vgs)),
|
331 |
+
})
|
332 |
+
if 1 <= is_over_one:
|
333 |
+
self.report(type={'INFO'}, message="A vertex whose total weight is more then 1 has been found. Please Normalize Weights." % is_over_one)
|
334 |
+
if 1 <= is_under_one:
|
335 |
+
self.report(type={'INFO'}, message="A vertex whose total weight is less then 1 has been found. Please Normalize Weights." % is_under_one)
|
336 |
+
context.window_manager.progress_update(4)
|
337 |
+
|
338 |
+
try:
|
339 |
+
file = common.open_temporary(self.filepath, 'wb', is_backup=self.is_backup)
|
340 |
+
except:
|
341 |
+
self.report(type={'ERROR'}, message="Failed to Backup previous file. Possibly inaccessible or non-existent.")
|
342 |
+
return {'CANCELLED'}
|
343 |
+
|
344 |
+
model_datas = {
|
345 |
+
'bone_data': bone_data,
|
346 |
+
'local_bone_data': local_bone_data,
|
347 |
+
'vertices': vertices,
|
348 |
+
}
|
349 |
+
try:
|
350 |
+
with file:
|
351 |
+
self.write_model(context, file, **model_datas)
|
352 |
+
except common.CM3D2ExportException as e:
|
353 |
+
self.report(type={'ERROR'}, message=str(e))
|
354 |
+
return {'CANCELLED'}
|
355 |
+
|
356 |
+
# モディファイアを適用する場合
|
357 |
+
if self.is_apply_modifiers:
|
358 |
+
context.blend_data.objects.remove(new_ob, do_unlink=True)
|
359 |
+
context.blend_data.meshes.remove(new_me, do_unlink=True)
|
360 |
+
context.scene.objects.active = source_ob
|
361 |
+
|
362 |
+
context.window_manager.progress_update(10)
|
363 |
+
diff_time = time.time() - start_time
|
364 |
+
self.report(type={'INFO'}, message=str(round(diff_time, 1)) + " Seconds")
|
365 |
+
self.report(type={'INFO'}, message="Model Exported Successfully. Mission Accomplished.")
|
366 |
+
return {'FINISHED'}
|
367 |
+
|
368 |
+
|
369 |
+
def write_model(self, context, file, bone_data=[], local_bone_data=[], vertices=[]):
|
370 |
+
"""モデルデータをファイルオブジェクトに書き込む"""
|
371 |
+
ob = context.active_object
|
372 |
+
me = ob.data
|
373 |
+
|
374 |
+
# ファイル先頭
|
375 |
+
common.write_str(file, 'CM3D2_MESH')
|
376 |
+
file.write(struct.pack('<i', self.version))
|
377 |
+
|
378 |
+
common.write_str(file, self.model_name)
|
379 |
+
common.write_str(file, self.base_bone_name)
|
380 |
+
|
381 |
+
# ボーン情報書き出し
|
382 |
+
file.write(struct.pack('<i', len(bone_data)))
|
383 |
+
for bone in bone_data:
|
384 |
+
common.write_str(file, bone['name'])
|
385 |
+
file.write(struct.pack('<b', bone['unknown']))
|
386 |
+
context.window_manager.progress_update(3.3)
|
387 |
+
for bone in bone_data:
|
388 |
+
file.write(struct.pack('<i', bone['parent_index']))
|
389 |
+
context.window_manager.progress_update(3.7)
|
390 |
+
for bone in bone_data:
|
391 |
+
file.write(struct.pack('<3f', bone['co'][0], bone['co'][1], bone['co'][2]))
|
392 |
+
file.write(struct.pack('<4f', bone['rot'][1], bone['rot'][2], bone['rot'][3], bone['rot'][0]))
|
393 |
+
context.window_manager.progress_update(4)
|
394 |
+
|
395 |
+
# 正しい頂点数などを取得
|
396 |
+
bm = bmesh.new()
|
397 |
+
bm.from_mesh(me)
|
398 |
+
uv_lay = bm.loops.layers.uv.active
|
399 |
+
vert_uvs = []
|
400 |
+
vert_uvs_append = vert_uvs.append
|
401 |
+
vert_iuv = {}
|
402 |
+
vert_indices = {}
|
403 |
+
vert_count = 0
|
404 |
+
for vert in bm.verts:
|
405 |
+
vert_uvs_append([])
|
406 |
+
for loop in vert.link_loops:
|
407 |
+
uv = loop[uv_lay].uv
|
408 |
+
if uv not in vert_uvs[-1]:
|
409 |
+
vert_uvs[-1].append(uv)
|
410 |
+
iuv_hash = hash(repr([vert.index, uv.x, uv.y]))
|
411 |
+
vert_iuv[iuv_hash] = vert_count
|
412 |
+
vert_indices[vert.index] = vert_count
|
413 |
+
vert_count += 1
|
414 |
+
if 65535 < vert_count:
|
415 |
+
raise common.CM3D2ExportException("There are still too many vertices (% d Vertices). Please remove% d more vertices. Aborting." % (vert_count, vert_count - 65535))
|
416 |
+
context.window_manager.progress_update(5)
|
417 |
+
|
418 |
+
file.write(struct.pack('<2i', vert_count, len(ob.material_slots)))
|
419 |
+
|
420 |
+
# ローカルボーン情報を書き出し
|
421 |
+
file.write(struct.pack('<i', len(local_bone_data)))
|
422 |
+
for bone in local_bone_data:
|
423 |
+
common.write_str(file, bone['name'])
|
424 |
+
context.window_manager.progress_update(5.3)
|
425 |
+
for bone in local_bone_data:
|
426 |
+
for f in bone['matrix']:
|
427 |
+
file.write(struct.pack('<f', f))
|
428 |
+
context.window_manager.progress_update(5.7)
|
429 |
+
|
430 |
+
# カスタム法線情報を取得
|
431 |
+
if me.has_custom_normals:
|
432 |
+
custom_normals = [mathutils.Vector() for i in range(len(me.vertices))]
|
433 |
+
me.calc_normals_split()
|
434 |
+
for loop in me.loops:
|
435 |
+
custom_normals[loop.vertex_index] = loop.normal.copy()
|
436 |
+
# 頂点情報を書き出し
|
437 |
+
for i, vert in enumerate(bm.verts):
|
438 |
+
co = vert.co * self.scale
|
439 |
+
if me.has_custom_normals:
|
440 |
+
no = custom_normals[vert.index]
|
441 |
+
else:
|
442 |
+
no = vert.normal.copy()
|
443 |
+
for uv in vert_uvs[i]:
|
444 |
+
file.write(struct.pack('<3f', -co.x, co.y, co.z))
|
445 |
+
file.write(struct.pack('<3f', -no.x, no.y, no.z))
|
446 |
+
file.write(struct.pack('<2f', uv.x, uv.y))
|
447 |
+
context.window_manager.progress_update(6)
|
448 |
+
|
449 |
+
# 不明な情報を書き出し
|
450 |
+
unknown_count = 0
|
451 |
+
file.write(struct.pack('<i', unknown_count))
|
452 |
+
|
453 |
+
# ウェイト情報を書き出し
|
454 |
+
for vert in vertices:
|
455 |
+
for uv in vert_uvs[vert['index']]:
|
456 |
+
file.write(struct.pack('<4H', *vert['face_indexs']))
|
457 |
+
file.write(struct.pack('<4f', *vert['weights']))
|
458 |
+
context.window_manager.progress_update(7)
|
459 |
+
|
460 |
+
# 面情報を書き出し
|
461 |
+
progress_plus_value = 1.0 / (len(ob.material_slots) * len(bm.faces))
|
462 |
+
progress_count = 7.0
|
463 |
+
progress_reduce = (len(ob.material_slots) * len(bm.faces)) // 200 + 1
|
464 |
+
|
465 |
+
def vert_index_from_loops(loops):
|
466 |
+
"""vert_index generator"""
|
467 |
+
for loop in loops:
|
468 |
+
uv = loop[uv_lay].uv
|
469 |
+
index = loop.vert.index
|
470 |
+
iuv_hash = hash(repr([index, uv.x, uv.y]))
|
471 |
+
vert_index = vert_iuv.get(iuv_hash)
|
472 |
+
if vert_index is None:
|
473 |
+
vert_index = vert_indices.get(index, 0)
|
474 |
+
yield vert_index
|
475 |
+
|
476 |
+
for mate_index, slot in enumerate(ob.material_slots):
|
477 |
+
tris_faces = []
|
478 |
+
for face in bm.faces:
|
479 |
+
progress_count += progress_plus_value
|
480 |
+
if face.index % progress_reduce == 0:
|
481 |
+
context.window_manager.progress_update(progress_count)
|
482 |
+
if face.material_index != mate_index:
|
483 |
+
continue
|
484 |
+
if len(face.verts) == 3:
|
485 |
+
tris_faces.extend(vert_index_from_loops(reversed(face.loops)))
|
486 |
+
elif len(face.verts) == 4 and self.is_convert_tris:
|
487 |
+
v1 = face.loops[0].vert.co - face.loops[2].vert.co
|
488 |
+
v2 = face.loops[1].vert.co - face.loops[3].vert.co
|
489 |
+
if v1.length < v2.length:
|
490 |
+
f1 = [0, 1, 2]
|
491 |
+
f2 = [0, 2, 3]
|
492 |
+
else:
|
493 |
+
f1 = [0, 1, 3]
|
494 |
+
f2 = [1, 2, 3]
|
495 |
+
faces, faces2 = [], []
|
496 |
+
for i, vert_index in enumerate(vert_index_from_loops(reversed(face.loops))):
|
497 |
+
if i in f1:
|
498 |
+
faces.append(vert_index)
|
499 |
+
if i in f2:
|
500 |
+
faces2.append(vert_index)
|
501 |
+
tris_faces.extend(faces)
|
502 |
+
tris_faces.extend(faces2)
|
503 |
+
elif 5 <= len(face.verts) and self.is_convert_tris:
|
504 |
+
face_count = len(face.verts) - 2
|
505 |
+
|
506 |
+
tris = []
|
507 |
+
seek_min, seek_max = 0, len(face.verts) - 1
|
508 |
+
for i in range(face_count):
|
509 |
+
if not i % 2:
|
510 |
+
tris.append([seek_min, seek_min+1, seek_max])
|
511 |
+
seek_min += 1
|
512 |
+
else:
|
513 |
+
tris.append([seek_min, seek_max-1, seek_max])
|
514 |
+
seek_max -= 1
|
515 |
+
|
516 |
+
tris_indexs = [[] for _ in range(len(tris))]
|
517 |
+
for i, vert_index in enumerate(vert_index_from_loops(reversed(face.loops))):
|
518 |
+
for tris_index, points in enumerate(tris):
|
519 |
+
if i in points:
|
520 |
+
tris_indexs[tris_index].append(vert_index)
|
521 |
+
|
522 |
+
tris_faces.extend(p for ps in tris_indexs for p in ps)
|
523 |
+
|
524 |
+
file.write(struct.pack('<i', len(tris_faces)))
|
525 |
+
for face_index in tris_faces:
|
526 |
+
file.write(struct.pack('<H', face_index))
|
527 |
+
context.window_manager.progress_update(8)
|
528 |
+
|
529 |
+
# マテリアルを書き出し
|
530 |
+
file.write(struct.pack('<i', len(ob.material_slots)))
|
531 |
+
for slot_index, slot in enumerate(ob.material_slots):
|
532 |
+
if self.mate_info_mode == 'MATERIAL':
|
533 |
+
mate = slot.material
|
534 |
+
common.write_str(file, common.remove_serial_number(mate.name, self.is_arrange_name))
|
535 |
+
common.write_str(file, mate['shader1'])
|
536 |
+
common.write_str(file, mate['shader2'])
|
537 |
+
for tindex, tslot in enumerate(mate.texture_slots):
|
538 |
+
if not tslot:
|
539 |
+
continue
|
540 |
+
tex = tslot.texture
|
541 |
+
if mate.use_textures[tindex]:
|
542 |
+
common.write_str(file, 'tex')
|
543 |
+
common.write_str(file, common.remove_serial_number(tex.name, self.is_arrange_name))
|
544 |
+
if tex.image:
|
545 |
+
img = tex.image
|
546 |
+
common.write_str(file, 'tex2d')
|
547 |
+
|
548 |
+
tex_name = common.remove_serial_number(img.name, self.is_arrange_name)
|
549 |
+
tex_name = re.sub(r"\.[Pp][Nn][Gg]$", "", tex_name)
|
550 |
+
common.write_str(file, tex_name)
|
551 |
+
|
552 |
+
if 'cm3d2_path' in img:
|
553 |
+
path = img['cm3d2_path']
|
554 |
+
else:
|
555 |
+
path = bpy.path.abspath(img.filepath)
|
556 |
+
path = path.replace('\\', '/')
|
557 |
+
path = re.sub(r'^[\/\.]*', "", path)
|
558 |
+
if not re.search(r'^assets/texture/', path, re.I):
|
559 |
+
path = "Assets/texture/texture/" + os.path.basename(path)
|
560 |
+
common.write_str(file, path)
|
561 |
+
col = tslot.color
|
562 |
+
file.write(struct.pack('<3f', col[0], col[1], col[2]))
|
563 |
+
file.write(struct.pack('<f', tslot.diffuse_color_factor))
|
564 |
+
else:
|
565 |
+
common.write_str(file, 'null')
|
566 |
+
else:
|
567 |
+
if tslot.use_rgb_to_intensity:
|
568 |
+
common.write_str(file, 'col')
|
569 |
+
common.write_str(file, common.remove_serial_number(tex.name, self.is_arrange_name))
|
570 |
+
col = tslot.color
|
571 |
+
file.write(struct.pack('<3f', col[0], col[1], col[2]))
|
572 |
+
file.write(struct.pack('<f', tslot.diffuse_color_factor))
|
573 |
+
else:
|
574 |
+
common.write_str(file, 'f')
|
575 |
+
common.write_str(file, common.remove_serial_number(tex.name, self.is_arrange_name))
|
576 |
+
file.write(struct.pack('<f', tslot.diffuse_color_factor))
|
577 |
+
elif self.mate_info_mode == 'TEXT':
|
578 |
+
data = context.blend_data.texts["Material:" + str(slot_index)].as_string()
|
579 |
+
data = data.split('\n')
|
580 |
+
common.write_str(file, data[2])
|
581 |
+
common.write_str(file, data[3])
|
582 |
+
common.write_str(file, data[4])
|
583 |
+
seek = 5
|
584 |
+
for i in range(9**9):
|
585 |
+
if len(data) <= seek:
|
586 |
+
break
|
587 |
+
type = data[seek]
|
588 |
+
if type == 'tex':
|
589 |
+
common.write_str(file, type)
|
590 |
+
common.write_str(file, common.line_trim(data[seek + 1]))
|
591 |
+
common.write_str(file, common.line_trim(data[seek + 2]))
|
592 |
+
if common.line_trim(data[seek + 2]) == 'tex2d':
|
593 |
+
common.write_str(file, common.line_trim(data[seek + 3]))
|
594 |
+
common.write_str(file, common.line_trim(data[seek + 4]))
|
595 |
+
col = common.line_trim(data[seek + 5])
|
596 |
+
col = col.split(' ')
|
597 |
+
file.write(struct.pack('<4f', float(col[0]), float(col[1]), float(col[2]), float(col[3])))
|
598 |
+
seek += 3
|
599 |
+
seek += 2
|
600 |
+
elif type == 'col':
|
601 |
+
common.write_str(file, type)
|
602 |
+
common.write_str(file, common.line_trim(data[seek + 1]))
|
603 |
+
col = common.line_trim(data[seek + 2])
|
604 |
+
col = col.split(' ')
|
605 |
+
file.write(struct.pack('<4f', float(col[0]), float(col[1]), float(col[2]), float(col[3])))
|
606 |
+
seek += 2
|
607 |
+
elif type == 'f':
|
608 |
+
common.write_str(file, type)
|
609 |
+
common.write_str(file, common.line_trim(data[seek + 1]))
|
610 |
+
file.write(struct.pack('<f', float(common.line_trim(data[seek + 2]))))
|
611 |
+
seek += 2
|
612 |
+
seek += 1
|
613 |
+
common.write_str(file, 'end')
|
614 |
+
context.window_manager.progress_update(9)
|
615 |
+
|
616 |
+
# モーフを書き出し
|
617 |
+
if me.shape_keys:
|
618 |
+
temp_me = context.blend_data.meshes.new(me.name + ".temp")
|
619 |
+
vs = [vert.co for vert in me.vertices]
|
620 |
+
es = []
|
621 |
+
fs = [face.vertices for face in me.polygons]
|
622 |
+
temp_me.from_pydata(vs, es, fs)
|
623 |
+
if 2 <= len(me.shape_keys.key_blocks):
|
624 |
+
for shape_key in me.shape_keys.key_blocks[1:]:
|
625 |
+
morph = []
|
626 |
+
vert_index = 0
|
627 |
+
for i in range(len(me.vertices)):
|
628 |
+
temp_me.vertices[i].co = shape_key.data[i].co.copy()
|
629 |
+
temp_me.update()
|
630 |
+
for i, vert in enumerate(me.vertices):
|
631 |
+
co_diff = shape_key.data[i].co - vert.co
|
632 |
+
if me.has_custom_normals:
|
633 |
+
no_diff = custom_normals[i] - vert.normal
|
634 |
+
else:
|
635 |
+
no_diff = temp_me.vertices[i].normal - vert.normal
|
636 |
+
if 0.001 < co_diff.length or 0.001 < no_diff.length:
|
637 |
+
co = co_diff * self.scale
|
638 |
+
for d in vert_uvs[i]:
|
639 |
+
morph.append((vert_index, co, no_diff))
|
640 |
+
vert_index += 1
|
641 |
+
else:
|
642 |
+
vert_index += len(vert_uvs[i])
|
643 |
+
if not len(morph):
|
644 |
+
continue
|
645 |
+
common.write_str(file, 'morph')
|
646 |
+
common.write_str(file, shape_key.name)
|
647 |
+
file.write(struct.pack('<i', len(morph)))
|
648 |
+
for index, vec, normal in morph:
|
649 |
+
file.write(struct.pack('<H', index))
|
650 |
+
file.write(struct.pack('<3f', -vec.x, vec.y, vec.z))
|
651 |
+
file.write(struct.pack('<3f', -normal.x, normal.y, normal.z))
|
652 |
+
context.blend_data.meshes.remove(temp_me)
|
653 |
+
common.write_str(file, 'end')
|
654 |
+
|
655 |
+
|
656 |
+
def select_no_weight_vertices(self, context, local_bone_name_indices):
|
657 |
+
"""ウェイトが割り当てられていない頂点を選択する"""
|
658 |
+
ob = context.active_object
|
659 |
+
me = ob.data
|
660 |
+
bpy.ops.object.mode_set(mode='EDIT')
|
661 |
+
bpy.ops.mesh.select_all(action='DESELECT')
|
662 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
663 |
+
context.tool_settings.mesh_select_mode = (True, False, False)
|
664 |
+
for vert in me.vertices:
|
665 |
+
for vg in vert.groups:
|
666 |
+
name = common.encode_bone_name(ob.vertex_groups[vg.group].name, self.is_convert_bone_weight_names)
|
667 |
+
if name in local_bone_name_indices and 0.0 < vg.weight:
|
668 |
+
break
|
669 |
+
else:
|
670 |
+
vert.select = True
|
671 |
+
bpy.ops.object.mode_set(mode='EDIT')
|
672 |
+
|
673 |
+
def armature_bone_data_parser(self, ob):
|
674 |
+
"""アーマチュアを解析してBoneDataを返す"""
|
675 |
+
arm = ob.data
|
676 |
+
|
677 |
+
bones = []
|
678 |
+
bone_name_indices = {}
|
679 |
+
already_bone_names = []
|
680 |
+
bones_queue = arm.bones[:]
|
681 |
+
while len(bones_queue):
|
682 |
+
bone = bones_queue.pop(0)
|
683 |
+
|
684 |
+
if not bone.parent:
|
685 |
+
already_bone_names.append(bone.name)
|
686 |
+
bones.append(bone)
|
687 |
+
bone_name_indices[bone.name] = len(bone_name_indices)
|
688 |
+
continue
|
689 |
+
elif bone.parent.name in already_bone_names:
|
690 |
+
already_bone_names.append(bone.name)
|
691 |
+
bones.append(bone)
|
692 |
+
bone_name_indices[bone.name] = len(bone_name_indices)
|
693 |
+
continue
|
694 |
+
|
695 |
+
bones_queue.append(bone)
|
696 |
+
|
697 |
+
bone_data = []
|
698 |
+
for bone in bones:
|
699 |
+
|
700 |
+
if 'UnknownFlag' in bone: unknown_frag = bone['UnknownFlag']
|
701 |
+
else: unknown_frag = 0
|
702 |
+
|
703 |
+
if bone.parent: parent_index = bone_name_indices[bone.parent.name]
|
704 |
+
else: parent_index = -1
|
705 |
+
|
706 |
+
mat = bone.matrix_local.copy()
|
707 |
+
if bone.parent:
|
708 |
+
mat = bone.parent.matrix_local.inverted() * mat
|
709 |
+
|
710 |
+
co = mat.to_translation() * self.scale
|
711 |
+
rot = mat.to_quaternion()
|
712 |
+
|
713 |
+
if bone.parent:
|
714 |
+
co.x, co.y, co.z = -co.y, -co.x, co.z
|
715 |
+
rot.w, rot.x, rot.y, rot.z = rot.w, rot.y, rot.x, -rot.z
|
716 |
+
else:
|
717 |
+
co.x, co.y, co.z = -co.x, co.z, -co.y
|
718 |
+
|
719 |
+
fix_quat = mathutils.Euler((0, 0, math.radians(-90)), 'XYZ').to_quaternion()
|
720 |
+
fix_quat2 = mathutils.Euler((math.radians(-90), 0, 0), 'XYZ').to_quaternion()
|
721 |
+
rot = rot * fix_quat * fix_quat2
|
722 |
+
|
723 |
+
rot.w, rot.x, rot.y, rot.z = -rot.y, -rot.z, -rot.x, rot.w
|
724 |
+
|
725 |
+
bone_data.append({
|
726 |
+
'name': bone.name,
|
727 |
+
'unknown': unknown_frag,
|
728 |
+
'parent_index': parent_index,
|
729 |
+
'co': co.copy(),
|
730 |
+
'rot': rot.copy(),
|
731 |
+
})
|
732 |
+
return bone_data
|
733 |
+
|
734 |
+
@staticmethod
|
735 |
+
def bone_data_parser(container):
|
736 |
+
"""BoneData テキストをパースして辞書を要素とするリストを返す"""
|
737 |
+
bone_data = []
|
738 |
+
bone_name_indices = {}
|
739 |
+
for line in container:
|
740 |
+
data = line.split(',')
|
741 |
+
if len(data) != 5:
|
742 |
+
continue
|
743 |
+
parent_name = data[2]
|
744 |
+
if parent_name.isdigit():
|
745 |
+
parent_index = int(parent_name)
|
746 |
+
else:
|
747 |
+
parent_index = bone_name_indices.get(parent_name, -1)
|
748 |
+
bone_data.append({
|
749 |
+
'name': data[0],
|
750 |
+
'unknown': int(data[1]),
|
751 |
+
'parent_index': parent_index,
|
752 |
+
'co': list(map(float, data[3].split())),
|
753 |
+
'rot': list(map(float, data[4].split())),
|
754 |
+
})
|
755 |
+
bone_name_indices[data[0]] = len(bone_name_indices)
|
756 |
+
return bone_data
|
757 |
+
|
758 |
+
def armature_local_bone_data_parser(self, ob):
|
759 |
+
"""アーマチュアを解析してBoneDataを返す"""
|
760 |
+
arm = ob.data
|
761 |
+
|
762 |
+
bones = []
|
763 |
+
bone_name_indices = {}
|
764 |
+
already_bone_names = []
|
765 |
+
bones_queue = arm.bones[:]
|
766 |
+
while len(bones_queue):
|
767 |
+
bone = bones_queue.pop(0)
|
768 |
+
|
769 |
+
if not bone.parent:
|
770 |
+
already_bone_names.append(bone.name)
|
771 |
+
bones.append(bone)
|
772 |
+
bone_name_indices[bone.name] = len(bone_name_indices)
|
773 |
+
continue
|
774 |
+
elif bone.parent.name in already_bone_names:
|
775 |
+
already_bone_names.append(bone.name)
|
776 |
+
bones.append(bone)
|
777 |
+
bone_name_indices[bone.name] = len(bone_name_indices)
|
778 |
+
continue
|
779 |
+
|
780 |
+
bones_queue.append(bone)
|
781 |
+
|
782 |
+
local_bone_data = []
|
783 |
+
for bone in bones:
|
784 |
+
|
785 |
+
mat = bone.matrix_local.copy()
|
786 |
+
|
787 |
+
co = mat.to_translation() * self.scale
|
788 |
+
rot = mat.to_quaternion()
|
789 |
+
|
790 |
+
co.rotate(rot.inverted())
|
791 |
+
co.x, co.y, co.z = co.y, co.x, -co.z
|
792 |
+
|
793 |
+
fix_quat = mathutils.Euler((0, 0, math.radians(-90)), 'XYZ').to_quaternion()
|
794 |
+
rot = rot * fix_quat
|
795 |
+
rot.w, rot.x, rot.y, rot.z = -rot.z, -rot.y, -rot.x, rot.w
|
796 |
+
|
797 |
+
co_mat = mathutils.Matrix.Translation(co)
|
798 |
+
rot_mat = rot.to_matrix().to_4x4()
|
799 |
+
mat = co_mat * rot_mat
|
800 |
+
|
801 |
+
copy_mat = mat.copy()
|
802 |
+
mat[0][0], mat[0][1], mat[0][2], mat[0][3] = copy_mat[0][0], copy_mat[1][0], copy_mat[2][0], copy_mat[3][0]
|
803 |
+
mat[1][0], mat[1][1], mat[1][2], mat[1][3] = copy_mat[0][1], copy_mat[1][1], copy_mat[2][1], copy_mat[3][1]
|
804 |
+
mat[2][0], mat[2][1], mat[2][2], mat[2][3] = copy_mat[0][2], copy_mat[1][2], copy_mat[2][2], copy_mat[3][2]
|
805 |
+
mat[3][0], mat[3][1], mat[3][2], mat[3][3] = copy_mat[0][3], copy_mat[1][3], copy_mat[2][3], copy_mat[3][3]
|
806 |
+
|
807 |
+
mat_array = []
|
808 |
+
for vec in mat:
|
809 |
+
mat_array.extend(vec[:])
|
810 |
+
|
811 |
+
local_bone_data.append({
|
812 |
+
'name': bone.name,
|
813 |
+
'matrix': mat_array,
|
814 |
+
})
|
815 |
+
return local_bone_data
|
816 |
+
|
817 |
+
@staticmethod
|
818 |
+
def local_bone_data_parser(container):
|
819 |
+
"""LocalBoneData テキストをパースして辞書を要素とするリストを返す"""
|
820 |
+
local_bone_data = []
|
821 |
+
for line in container:
|
822 |
+
data = line.split(',')
|
823 |
+
if len(data) != 2:
|
824 |
+
continue
|
825 |
+
local_bone_data.append({
|
826 |
+
'name': data[0],
|
827 |
+
'matrix': list(map(float, data[1].split())),
|
828 |
+
})
|
829 |
+
return local_bone_data
|
830 |
+
|
831 |
+
|
832 |
+
@staticmethod
|
833 |
+
def indexed_data_generator(container, prefix='', max_index=9**9, max_pass=50):
|
834 |
+
"""コンテナ内の数値インデックスをキーに持つ要素を昇順に返すジェネレーター"""
|
835 |
+
pass_count = 0
|
836 |
+
for i in range(max_index):
|
837 |
+
name = prefix + str(i)
|
838 |
+
if name not in container:
|
839 |
+
pass_count += 1
|
840 |
+
if max_pass < pass_count:
|
841 |
+
return
|
842 |
+
continue
|
843 |
+
yield container[name]
|
844 |
+
|
845 |
+
|
846 |
+
|
847 |
+
# メニューを登録する関数
|
848 |
+
def menu_func(self, context):
|
849 |
+
self.layout.operator(export_cm3d2_model.bl_idname, icon_value=common.preview_collections['main']['KISS'].icon_id)
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/model_import.py
ADDED
@@ -0,0 +1,671 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import bpy, bmesh, mathutils
|
2 |
+
import math, struct, time, os
|
3 |
+
from collections import Counter
|
4 |
+
from . import common
|
5 |
+
|
6 |
+
# メインオペレーター
|
7 |
+
class import_cm3d2_model(bpy.types.Operator):
|
8 |
+
bl_idname = 'import_mesh.import_cm3d2_model'
|
9 |
+
bl_label = "CM3D2 Model (.model)"
|
10 |
+
bl_description = "Imports a model from the game CM3D2"
|
11 |
+
bl_options = {'REGISTER'}
|
12 |
+
|
13 |
+
filepath = bpy.props.StringProperty(subtype='FILE_PATH')
|
14 |
+
filename_ext = ".model"
|
15 |
+
filter_glob = bpy.props.StringProperty(default="*.model", options={'HIDDEN'})
|
16 |
+
|
17 |
+
scale = bpy.props.FloatProperty(name="Scale", default=5, min=0.1, max=100, soft_min=0.1, soft_max=100, step=100, precision=1, description="The amount by which the mesh is scaled when imported. Recommended that you use the same when at the time of export.")
|
18 |
+
|
19 |
+
is_mesh = bpy.props.BoolProperty(name="Load Mesh", default=True, description="Leaving this on will load in the mesh.")
|
20 |
+
is_remove_doubles = bpy.props.BoolProperty(name="Remove Doubles", default=True, description="Doubles will be removed in both the uv and the mesh at the time of import.")
|
21 |
+
is_seam = bpy.props.BoolProperty(name="Mark Seams", default=True, description="This will mark the UV seams on your mesh.")
|
22 |
+
|
23 |
+
is_convert_bone_weight_names = bpy.props.BoolProperty(name="Convert Bone Weight Names to Blender", default=False, description="This will convert bone and vertex group names for use with blender mirroring.")
|
24 |
+
is_vertex_group_sort = bpy.props.BoolProperty(name="Sort Vertex Groups", default=True, description="This will sort your vertex groups so they are easier to work with.")
|
25 |
+
is_remove_empty_vertex_group = bpy.props.BoolProperty(name="Remove Empty Vertex Groups", default=True, description="Will remove any empty vertex groups to which no vertices are assigned to it.")
|
26 |
+
|
27 |
+
is_replace_cm3d2_tex = bpy.props.BoolProperty(name="Find Textures", default=True, description="Will search the CM3D2 folder for the textures. (Textures must be in .png format to be loaded)")
|
28 |
+
is_decorate = bpy.props.BoolProperty(name="Decorate Material", default=True)
|
29 |
+
is_mate_data_text = bpy.props.BoolProperty(name="Export Mate Data to text editor.", default=True, description="Material information will be placed into the text editor.")
|
30 |
+
|
31 |
+
is_armature = bpy.props.BoolProperty(name="Load Armature", default=True, description="If left, it will load in the corresponding Armature.")
|
32 |
+
is_armature_clean = bpy.props.BoolProperty(name="Clean Armature", default=True, description="Will delete any unneeded bones.")
|
33 |
+
|
34 |
+
is_bone_data_text = bpy.props.BoolProperty(name="Bone Data to Text", default=True, description="All of the bone data will be also placed into the text editor.")
|
35 |
+
is_bone_data_obj_property = bpy.props.BoolProperty(name="Bone Data to Object Properties", default=True, description="Bone Data will also be placed in the Objects custom properties.")
|
36 |
+
is_bone_data_arm_property = bpy.props.BoolProperty(name="Bone Data to Armature Properties", default=True, description="Bone data will also be placed in the Armatures custom properties.")
|
37 |
+
|
38 |
+
@classmethod
|
39 |
+
def poll(cls, context):
|
40 |
+
return True
|
41 |
+
|
42 |
+
def invoke(self, context, event):
|
43 |
+
if common.preferences().model_default_path:
|
44 |
+
self.filepath = common.default_cm3d2_dir(common.preferences().model_default_path, "", "model")
|
45 |
+
else:
|
46 |
+
self.filepath = common.default_cm3d2_dir(common.preferences().model_import_path, "", "model")
|
47 |
+
self.scale = common.preferences().scale
|
48 |
+
self.is_replace_cm3d2_tex = common.preferences().is_replace_cm3d2_tex
|
49 |
+
self.is_convert_bone_weight_names = common.preferences().is_convert_bone_weight_names
|
50 |
+
context.window_manager.fileselect_add(self)
|
51 |
+
return {'RUNNING_MODAL'}
|
52 |
+
|
53 |
+
def draw(self, context):
|
54 |
+
self.layout.prop(self, 'scale')
|
55 |
+
box = self.layout.box()
|
56 |
+
box.prop(self, 'is_mesh', icon='MESH_DATA')
|
57 |
+
sub_box = box.box()
|
58 |
+
sub_box.label("Mesh")
|
59 |
+
sub_box.prop(self, 'is_remove_doubles', icon='STICKY_UVS_VERT')
|
60 |
+
sub_box.prop(self, 'is_seam', icon='KEY_DEHLT')
|
61 |
+
sub_box = box.box()
|
62 |
+
sub_box.label("Vertex Group")
|
63 |
+
sub_box.prop(self, 'is_vertex_group_sort', icon='SORTALPHA')
|
64 |
+
sub_box.prop(self, 'is_remove_empty_vertex_group', icon='DISCLOSURE_TRI_DOWN')
|
65 |
+
sub_box.prop(self, 'is_convert_bone_weight_names', icon='BLENDER')
|
66 |
+
sub_box = box.box()
|
67 |
+
sub_box.label("Material")
|
68 |
+
sub_box.prop(self, 'is_replace_cm3d2_tex', icon='BORDERMOVE')
|
69 |
+
sub_box.prop(self, 'is_decorate', icon='TEXTURE_SHADED')
|
70 |
+
sub_box.prop(self, 'is_mate_data_text', icon='TEXT')
|
71 |
+
box = self.layout.box()
|
72 |
+
box.prop(self, 'is_armature', icon='ARMATURE_DATA')
|
73 |
+
sub_box = box.box()
|
74 |
+
sub_box.label("Armature")
|
75 |
+
sub_box.prop(self, 'is_armature_clean', icon='X')
|
76 |
+
sub_box.prop(self, 'is_convert_bone_weight_names', icon='BLENDER', text="Convert Bone Names for Blender.")
|
77 |
+
box = self.layout.box()
|
78 |
+
box.label("Bone Data Destination")
|
79 |
+
box.prop(self, 'is_bone_data_text', icon='TEXT')
|
80 |
+
box.prop(self, 'is_bone_data_obj_property', icon='OBJECT_DATA')
|
81 |
+
box.prop(self, 'is_bone_data_arm_property', icon='ARMATURE_DATA')
|
82 |
+
|
83 |
+
def execute(self, context):
|
84 |
+
start_time = time.time()
|
85 |
+
|
86 |
+
common.preferences().model_import_path = self.filepath
|
87 |
+
common.preferences().scale = self.scale
|
88 |
+
context.window_manager.progress_begin(0, 10)
|
89 |
+
context.window_manager.progress_update(0)
|
90 |
+
|
91 |
+
try:
|
92 |
+
file = open(self.filepath, 'rb')
|
93 |
+
except:
|
94 |
+
self.report(type={'ERROR'}, message="Failed to open file. The file is inaccessible or does not exist.")
|
95 |
+
return {'CANCELLED'}
|
96 |
+
|
97 |
+
# ヘッダー
|
98 |
+
ext = common.read_str(file)
|
99 |
+
if ext != 'CM3D2_MESH':
|
100 |
+
self.report(type={'ERROR'}, message="This is not a CM3D2 Model File.")
|
101 |
+
return {'CANCELLED'}
|
102 |
+
struct.unpack('<i', file.read(4))[0]
|
103 |
+
context.window_manager.progress_update(0.1)
|
104 |
+
|
105 |
+
# 名前群を取得
|
106 |
+
model_name1 = common.read_str(file)
|
107 |
+
model_name2 = common.read_str(file)
|
108 |
+
context.window_manager.progress_update(0.2)
|
109 |
+
|
110 |
+
# ボーン情報読み込み
|
111 |
+
bone_data = []
|
112 |
+
bone_count = struct.unpack('<i', file.read(4))[0]
|
113 |
+
for i in range(bone_count):
|
114 |
+
bone_data.append({})
|
115 |
+
bone_data[i]['name'] = common.read_str(file)
|
116 |
+
bone_data[i]['unknown'] = struct.unpack('<B', file.read(1))[0]
|
117 |
+
for i in range(bone_count):
|
118 |
+
parent_index = struct.unpack('<i', file.read(4))[0]
|
119 |
+
parent_name = None
|
120 |
+
if parent_index != -1:
|
121 |
+
parent_name = bone_data[parent_index]['name']
|
122 |
+
bone_data[i]['parent_index'] = parent_index
|
123 |
+
bone_data[i]['parent_name'] = parent_name
|
124 |
+
for i in range(bone_count):
|
125 |
+
x, y, z = struct.unpack('<3f', file.read(3*4))
|
126 |
+
bone_data[i]['co'] = mathutils.Vector((x, y, z))
|
127 |
+
|
128 |
+
x, y, z = struct.unpack('<3f', file.read(3*4))
|
129 |
+
w = struct.unpack('<f', file.read(4))[0]
|
130 |
+
bone_data[i]['rot'] = mathutils.Quaternion((w, x, y, z))
|
131 |
+
context.window_manager.progress_update(0.3)
|
132 |
+
|
133 |
+
vertex_count, mesh_count, local_bone_count = struct.unpack('<3i', file.read(3*4))
|
134 |
+
|
135 |
+
# ローカルボーン情報読み込み
|
136 |
+
local_bone_data = []
|
137 |
+
for i in range(local_bone_count):
|
138 |
+
local_bone_data.append({})
|
139 |
+
local_bone_data[i]['name'] = common.read_str(file)
|
140 |
+
for i in range(local_bone_count):
|
141 |
+
row0 = struct.unpack('<4f', file.read(4*4))
|
142 |
+
row1 = struct.unpack('<4f', file.read(4*4))
|
143 |
+
row2 = struct.unpack('<4f', file.read(4*4))
|
144 |
+
row3 = struct.unpack('<4f', file.read(4*4))
|
145 |
+
local_bone_data[i]['matrix'] = mathutils.Matrix([row0, row1, row2, row3])
|
146 |
+
context.window_manager.progress_update(0.4)
|
147 |
+
|
148 |
+
# 頂点情報読み込み
|
149 |
+
vertex_data = []
|
150 |
+
for i in range(vertex_count):
|
151 |
+
co = struct.unpack('<3f', file.read(3*4))
|
152 |
+
no = struct.unpack('<3f', file.read(3*4))
|
153 |
+
uv = struct.unpack('<2f', file.read(2*4))
|
154 |
+
vertex_data.append({'co': co, 'normal': no, 'uv': uv})
|
155 |
+
comparison_data = list(hash(repr(v['co']) + " " + repr(v['normal'])) for v in vertex_data)
|
156 |
+
comparison_counter = Counter(comparison_data)
|
157 |
+
comparison_data = list((comparison_counter[h] > 1) for h in comparison_data)
|
158 |
+
del comparison_counter
|
159 |
+
unknown_count = struct.unpack('<i', file.read(4))[0]
|
160 |
+
for i in range(unknown_count):
|
161 |
+
struct.unpack('<4f', file.read(4*4))
|
162 |
+
for i in range(vertex_count):
|
163 |
+
indexes = struct.unpack('<4H', file.read(4*2))
|
164 |
+
values = struct.unpack('<4f', file.read(4*4))
|
165 |
+
vertex_data[i]['weights'] = list({
|
166 |
+
'index': index,
|
167 |
+
'value': value,
|
168 |
+
'name': local_bone_data[index]['name'],
|
169 |
+
} for index, value in zip(indexes, values))
|
170 |
+
context.window_manager.progress_update(0.5)
|
171 |
+
|
172 |
+
# 面情報読み込み
|
173 |
+
face_data = []
|
174 |
+
for i in range(mesh_count):
|
175 |
+
face_count = int(struct.unpack('<i', file.read(4))[0] / 3)
|
176 |
+
face_data.append([tuple(reversed(struct.unpack('<3H', file.read(3*2)))) for j in range(face_count)])
|
177 |
+
context.window_manager.progress_update(0.6)
|
178 |
+
|
179 |
+
# マテリアル情報読み込み
|
180 |
+
material_data = []
|
181 |
+
material_count = struct.unpack('<i', file.read(4))[0]
|
182 |
+
for i in range(material_count):
|
183 |
+
material_data.append({})
|
184 |
+
material_data[i]['name1'] = common.read_str(file)
|
185 |
+
material_data[i]['name2'] = common.read_str(file)
|
186 |
+
material_data[i]['name3'] = common.read_str(file)
|
187 |
+
material_data[i]['data'] = []
|
188 |
+
while True:
|
189 |
+
data_type = common.read_str(file)
|
190 |
+
if data_type == 'tex':
|
191 |
+
material_data[i]['data'].append({'type':data_type})
|
192 |
+
material_data[i]['data'][-1]['name'] = common.read_str(file)
|
193 |
+
material_data[i]['data'][-1]['type2'] = common.read_str(file)
|
194 |
+
if material_data[i]['data'][-1]['type2'] == 'tex2d':
|
195 |
+
material_data[i]['data'][-1]['name2'] = common.read_str(file)
|
196 |
+
material_data[i]['data'][-1]['path'] = common.read_str(file)
|
197 |
+
material_data[i]['data'][-1]['color'] = struct.unpack('<4f', file.read(4*4))
|
198 |
+
elif data_type == 'col':
|
199 |
+
material_data[i]['data'].append({'type':data_type})
|
200 |
+
material_data[i]['data'][-1]['name'] = common.read_str(file)
|
201 |
+
material_data[i]['data'][-1]['color'] = struct.unpack('<4f', file.read(4*4))
|
202 |
+
elif data_type == 'f':
|
203 |
+
material_data[i]['data'].append({'type':data_type})
|
204 |
+
material_data[i]['data'][-1]['name'] = common.read_str(file)
|
205 |
+
material_data[i]['data'][-1]['float'] = struct.unpack('<f', file.read(4))[0]
|
206 |
+
else:
|
207 |
+
break
|
208 |
+
context.window_manager.progress_update(0.8)
|
209 |
+
|
210 |
+
# その他情報読み込み
|
211 |
+
misc_data = []
|
212 |
+
while True:
|
213 |
+
data_type = common.read_str(file)
|
214 |
+
if data_type == 'morph':
|
215 |
+
misc_data.append({'type':data_type})
|
216 |
+
misc_data[-1]['name'] = common.read_str(file)
|
217 |
+
morph_vert_count = struct.unpack('<i', file.read(4))[0]
|
218 |
+
misc_data[-1]['data'] = []
|
219 |
+
for i in range(morph_vert_count):
|
220 |
+
misc_data[-1]['data'].append({})
|
221 |
+
misc_data[-1]['data'][i]['index'] = struct.unpack('<H', file.read(2))[0]
|
222 |
+
misc_data[-1]['data'][i]['co'] = mathutils.Vector(struct.unpack('<3f', file.read(3*4)))
|
223 |
+
misc_data[-1]['data'][i]['normal'] = struct.unpack('<3f', file.read(3*4))
|
224 |
+
else:
|
225 |
+
break
|
226 |
+
|
227 |
+
file.close()
|
228 |
+
context.window_manager.progress_update(1)
|
229 |
+
|
230 |
+
try:
|
231 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
232 |
+
except RuntimeError:
|
233 |
+
pass
|
234 |
+
bpy.ops.object.select_all(action='DESELECT')
|
235 |
+
|
236 |
+
# アーマチュア作成
|
237 |
+
if self.is_armature:
|
238 |
+
arm = bpy.data.armatures.new(model_name1 + ".armature")
|
239 |
+
arm_ob = bpy.data.objects.new(model_name1 + ".armature", arm)
|
240 |
+
bpy.context.scene.objects.link(arm_ob)
|
241 |
+
arm_ob.select = True
|
242 |
+
bpy.context.scene.objects.active = arm_ob
|
243 |
+
bpy.ops.object.mode_set(mode='EDIT')
|
244 |
+
|
245 |
+
# 基幹ボーンのみ作成
|
246 |
+
child_data = []
|
247 |
+
for data in bone_data:
|
248 |
+
if not data['parent_name']:
|
249 |
+
bone = arm.edit_bones.new(common.decode_bone_name(data['name'], self.is_convert_bone_weight_names))
|
250 |
+
bone.head, bone.tail = (0, 0, 0), (0, 1, 0)
|
251 |
+
|
252 |
+
co = data['co'].copy() * self.scale
|
253 |
+
rot = data['rot']
|
254 |
+
|
255 |
+
co_mat = mathutils.Matrix.Translation(co)
|
256 |
+
rot_mat = rot.to_matrix().to_4x4()
|
257 |
+
mat = co_mat * rot_mat
|
258 |
+
|
259 |
+
fix_mat_scale = mathutils.Matrix.Scale(-1, 4, (1, 0, 0))
|
260 |
+
fix_mat_before = mathutils.Euler((math.radians(90), 0, 0), 'XYZ').to_matrix().to_4x4()
|
261 |
+
fix_mat_after = mathutils.Euler((0, 0, math.radians(90)), 'XYZ').to_matrix().to_4x4()
|
262 |
+
|
263 |
+
bone.matrix = fix_mat_scale * fix_mat_before * mat * fix_mat_after
|
264 |
+
|
265 |
+
if data['unknown']: bone["UnknownFlag"] = 1
|
266 |
+
else: bone["UnknownFlag"] = 0
|
267 |
+
else:
|
268 |
+
child_data.append(data)
|
269 |
+
context.window_manager.progress_update(1.333)
|
270 |
+
|
271 |
+
# 子ボーンを追加していく
|
272 |
+
while len(child_data):
|
273 |
+
data = child_data.pop(0)
|
274 |
+
if common.decode_bone_name(data['parent_name'], self.is_convert_bone_weight_names) in arm.edit_bones:
|
275 |
+
bone = arm.edit_bones.new(common.decode_bone_name(data['name'], self.is_convert_bone_weight_names))
|
276 |
+
parent = arm.edit_bones[common.decode_bone_name(data['parent_name'], self.is_convert_bone_weight_names)]
|
277 |
+
bone.parent = parent
|
278 |
+
bone.head, bone.tail = (0, 0, 0), (0, 1, 0)
|
279 |
+
|
280 |
+
parent_mats = []
|
281 |
+
current_bone = bone
|
282 |
+
while current_bone:
|
283 |
+
for b in bone_data:
|
284 |
+
if common.decode_bone_name(b['name'], self.is_convert_bone_weight_names) == current_bone.name:
|
285 |
+
local_co = b['co'].copy()
|
286 |
+
local_rot = b['rot'].copy()
|
287 |
+
break
|
288 |
+
|
289 |
+
local_co_mat = mathutils.Matrix.Translation(local_co)
|
290 |
+
local_rot_mat = local_rot.to_matrix().to_4x4()
|
291 |
+
parent_mats.append(local_co_mat * local_rot_mat)
|
292 |
+
|
293 |
+
current_bone = current_bone.parent
|
294 |
+
parent_mats.reverse()
|
295 |
+
|
296 |
+
mat = mathutils.Matrix()
|
297 |
+
for local_mat in parent_mats:
|
298 |
+
mat *= local_mat
|
299 |
+
mat *= self.scale
|
300 |
+
|
301 |
+
fix_mat_scale = mathutils.Matrix.Scale(-1, 4, (1, 0, 0))
|
302 |
+
fix_mat_before = mathutils.Euler((math.radians(90), 0, 0), 'XYZ').to_matrix().to_4x4()
|
303 |
+
fix_mat_after = mathutils.Euler((0, 0, math.radians(90)), 'XYZ').to_matrix().to_4x4()
|
304 |
+
|
305 |
+
bone.matrix = fix_mat_scale * fix_mat_before * mat * fix_mat_after
|
306 |
+
|
307 |
+
if data['unknown']: bone["UnknownFlag"] = 1
|
308 |
+
else: bone["UnknownFlag"] = 0
|
309 |
+
else:
|
310 |
+
child_data.append(data)
|
311 |
+
context.window_manager.progress_update(1.666)
|
312 |
+
|
313 |
+
# ボーン整頓
|
314 |
+
for bone in arm.edit_bones:
|
315 |
+
if len(bone.children) == 0:
|
316 |
+
if bone.parent:
|
317 |
+
pass
|
318 |
+
else:
|
319 |
+
bone.length = 0.2 * self.scale
|
320 |
+
elif len(bone.children) == 1:
|
321 |
+
co = bone.children[0].head - bone.head
|
322 |
+
bone.length = co.length
|
323 |
+
elif len(bone.children) >= 2:
|
324 |
+
if bone.parent:
|
325 |
+
max_len = 0.0
|
326 |
+
for child_bone in bone.children:
|
327 |
+
co = child_bone.head - bone.head
|
328 |
+
if max_len < co.length:
|
329 |
+
max_len = co.length
|
330 |
+
bone.length = max_len
|
331 |
+
else:
|
332 |
+
bone.length = 0.2 * self.scale
|
333 |
+
for bone in arm.edit_bones:
|
334 |
+
if len(bone.children) == 0:
|
335 |
+
if bone.parent:
|
336 |
+
bone.length = bone.parent.length * 0.5
|
337 |
+
|
338 |
+
# 一部ボーン削除
|
339 |
+
if self.is_armature_clean:
|
340 |
+
for bone in arm.edit_bones:
|
341 |
+
for b in local_bone_data:
|
342 |
+
name = common.decode_bone_name(b['name'], self.is_convert_bone_weight_names)
|
343 |
+
if bone.name == name:
|
344 |
+
break
|
345 |
+
else:
|
346 |
+
arm.edit_bones.remove(bone)
|
347 |
+
|
348 |
+
arm.layers[16] = True
|
349 |
+
arm.draw_type = 'STICK'
|
350 |
+
arm_ob.show_x_ray = True
|
351 |
+
bpy.ops.armature.select_all(action='DESELECT')
|
352 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
353 |
+
context.window_manager.progress_update(2)
|
354 |
+
|
355 |
+
if self.is_mesh:
|
356 |
+
# メッシュ作成
|
357 |
+
me = context.blend_data.meshes.new(model_name1)
|
358 |
+
verts, faces = [], []
|
359 |
+
for data in vertex_data:
|
360 |
+
co = list(data['co'][:])
|
361 |
+
co[0] = -co[0]
|
362 |
+
co[0] *= self.scale
|
363 |
+
co[1] *= self.scale
|
364 |
+
co[2] *= self.scale
|
365 |
+
verts.append(co)
|
366 |
+
context.window_manager.progress_update(2.25)
|
367 |
+
for data in face_data:
|
368 |
+
faces.extend(data)
|
369 |
+
context.window_manager.progress_update(2.5)
|
370 |
+
me.from_pydata(verts, [], faces)
|
371 |
+
# オブジェクト化
|
372 |
+
ob = context.blend_data.objects.new(model_name1, me)
|
373 |
+
context.scene.objects.link(ob)
|
374 |
+
ob.select = True
|
375 |
+
context.scene.objects.active = ob
|
376 |
+
bpy.ops.object.shade_smooth()
|
377 |
+
context.window_manager.progress_update(2.75)
|
378 |
+
# オブジェクト変形
|
379 |
+
for bone in bone_data:
|
380 |
+
if bone['name'] == model_name2:
|
381 |
+
co = bone['co'].copy()
|
382 |
+
co.x, co.y, co.z = -co.x, -co.z, co.y
|
383 |
+
co *= self.scale
|
384 |
+
ob.location = co
|
385 |
+
|
386 |
+
rot = bone['rot'].copy()
|
387 |
+
eul = mathutils.Euler((math.radians(90), 0, 0), 'XYZ')
|
388 |
+
rot.rotate(eul)
|
389 |
+
ob.rotation_mode = 'QUATERNION'
|
390 |
+
ob.rotation_quaternion = rot
|
391 |
+
|
392 |
+
break
|
393 |
+
context.window_manager.progress_update(3)
|
394 |
+
|
395 |
+
# 頂点グループ作成
|
396 |
+
for data in local_bone_data:
|
397 |
+
ob.vertex_groups.new(common.decode_bone_name(data['name'], self.is_convert_bone_weight_names))
|
398 |
+
context.window_manager.progress_update(3.333)
|
399 |
+
for vert_index, data in enumerate(vertex_data):
|
400 |
+
for weight in data['weights']:
|
401 |
+
if 0.0 < weight['value']:
|
402 |
+
vertex_group = ob.vertex_groups[common.decode_bone_name(weight['name'], self.is_convert_bone_weight_names)]
|
403 |
+
vertex_group.add([vert_index], weight['value'], 'REPLACE')
|
404 |
+
context.window_manager.progress_update(3.666)
|
405 |
+
if self.is_vertex_group_sort:
|
406 |
+
bpy.ops.object.vertex_group_sort(sort_type='NAME')
|
407 |
+
if self.is_remove_empty_vertex_group:
|
408 |
+
for vg in ob.vertex_groups[:]:
|
409 |
+
for vert in me.vertices:
|
410 |
+
for group in vert.groups:
|
411 |
+
if group.group == vg.index:
|
412 |
+
if 0.0 < group.weight:
|
413 |
+
break
|
414 |
+
else:
|
415 |
+
continue
|
416 |
+
break
|
417 |
+
else:
|
418 |
+
ob.vertex_groups.remove(vg)
|
419 |
+
ob.vertex_groups.active_index = 0
|
420 |
+
context.window_manager.progress_update(4)
|
421 |
+
|
422 |
+
# UV作成
|
423 |
+
bpy.ops.mesh.uv_texture_add()
|
424 |
+
bm = bmesh.new()
|
425 |
+
bm.from_mesh(me)
|
426 |
+
for face in bm.faces:
|
427 |
+
for loop in face.loops:
|
428 |
+
loop[bm.loops.layers.uv.active].uv = vertex_data[loop.vert.index]['uv']
|
429 |
+
bm.to_mesh(me)
|
430 |
+
bm.free()
|
431 |
+
context.window_manager.progress_update(5)
|
432 |
+
|
433 |
+
# モーフ追加
|
434 |
+
morph_count = 0
|
435 |
+
for data in misc_data:
|
436 |
+
if data['type'] == 'morph':
|
437 |
+
if morph_count == 0:
|
438 |
+
bpy.ops.object.shape_key_add(from_mix=False)
|
439 |
+
me.shape_keys.name = model_name1
|
440 |
+
shape_key = ob.shape_key_add(name=data['name'], from_mix=False)
|
441 |
+
for vert in data['data']:
|
442 |
+
co = vert['co']
|
443 |
+
co.x = -co.x
|
444 |
+
co *= self.scale
|
445 |
+
shape_key.data[vert['index']].co = shape_key.data[vert['index']].co + co
|
446 |
+
morph_count += 1
|
447 |
+
context.window_manager.progress_update(6)
|
448 |
+
|
449 |
+
# マテリアル追加
|
450 |
+
progress_count_total = 0.0
|
451 |
+
for data in material_data:
|
452 |
+
progress_count_total += len(data['data'])
|
453 |
+
progress_plus_value = 1.0 / progress_count_total
|
454 |
+
progress_count = 6.0
|
455 |
+
|
456 |
+
tex_storage_files = common.get_tex_storage_files()
|
457 |
+
|
458 |
+
face_seek = 0
|
459 |
+
for index, data in enumerate(material_data):
|
460 |
+
override = context.copy()
|
461 |
+
override['object'] = ob
|
462 |
+
bpy.ops.object.material_slot_add(override)
|
463 |
+
mate = context.blend_data.materials.new(data['name1'])
|
464 |
+
mate['shader1'] = data['name2']
|
465 |
+
mate['shader2'] = data['name3']
|
466 |
+
|
467 |
+
ob.material_slots[-1].material = mate
|
468 |
+
# 面にマテリアル割り当て
|
469 |
+
for i in range(face_seek, face_seek + len(face_data[index])):
|
470 |
+
me.polygons[i].material_index = index
|
471 |
+
face_seek += len(face_data[index])
|
472 |
+
|
473 |
+
# テクスチャ追加
|
474 |
+
already_texs = []
|
475 |
+
tex_index = 0
|
476 |
+
for tex_data in data['data']:
|
477 |
+
|
478 |
+
if common.preferences().mate_unread_same_value:
|
479 |
+
if tex_data['name'] in already_texs:
|
480 |
+
continue
|
481 |
+
already_texs.append(tex_data['name'])
|
482 |
+
|
483 |
+
if tex_data['type'] == 'tex':
|
484 |
+
slot = mate.texture_slots.create(tex_index)
|
485 |
+
tex = context.blend_data.textures.new(tex_data['name'], 'IMAGE')
|
486 |
+
slot.texture = tex
|
487 |
+
if tex_data['type2'] == 'tex2d':
|
488 |
+
slot.color = tex_data['color'][:3]
|
489 |
+
slot.diffuse_color_factor = tex_data['color'][3]
|
490 |
+
img = context.blend_data.images.new(tex_data['name2'], 128, 128)
|
491 |
+
img.filepath = tex_data['path']
|
492 |
+
img['cm3d2_path'] = tex_data['path']
|
493 |
+
img.source = 'FILE'
|
494 |
+
tex.image = img
|
495 |
+
|
496 |
+
# tex探し
|
497 |
+
if self.is_replace_cm3d2_tex:
|
498 |
+
if common.replace_cm3d2_tex(img, tex_storage_files) and tex_data['name']=='_MainTex':
|
499 |
+
for face in me.polygons:
|
500 |
+
if face.material_index == index:
|
501 |
+
me.uv_textures.active.data[face.index].image = img
|
502 |
+
|
503 |
+
elif tex_data['type'] == 'col':
|
504 |
+
slot = mate.texture_slots.create(tex_index)
|
505 |
+
mate.use_textures[tex_index] = False
|
506 |
+
slot.color = tex_data['color'][:3]
|
507 |
+
slot.diffuse_color_factor = tex_data['color'][3]
|
508 |
+
slot.use_rgb_to_intensity = True
|
509 |
+
tex = context.blend_data.textures.new(tex_data['name'], 'BLEND')
|
510 |
+
slot.texture = tex
|
511 |
+
|
512 |
+
elif tex_data['type'] == 'f':
|
513 |
+
slot = mate.texture_slots.create(tex_index)
|
514 |
+
mate.use_textures[tex_index] = False
|
515 |
+
slot.diffuse_color_factor = tex_data['float']
|
516 |
+
tex = context.blend_data.textures.new(tex_data['name'], 'BLEND')
|
517 |
+
slot.texture = tex
|
518 |
+
|
519 |
+
tex_index += 1
|
520 |
+
|
521 |
+
progress_count += progress_plus_value
|
522 |
+
context.window_manager.progress_update(progress_count)
|
523 |
+
common.decorate_material(mate, self.is_decorate, me, index)
|
524 |
+
ob.active_material_index = 0
|
525 |
+
context.window_manager.progress_update(7)
|
526 |
+
|
527 |
+
# メッシュ整頓
|
528 |
+
if self.is_remove_doubles:
|
529 |
+
pre_mesh_select_mode = context.tool_settings.mesh_select_mode[:]
|
530 |
+
context.tool_settings.mesh_select_mode = (True, False, False)
|
531 |
+
|
532 |
+
bpy.ops.object.mode_set(mode='EDIT')
|
533 |
+
bpy.ops.mesh.select_all(action='DESELECT')
|
534 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
535 |
+
|
536 |
+
for is_comparison, vert in zip(comparison_data, me.vertices):
|
537 |
+
if is_comparison:
|
538 |
+
vert.select = True
|
539 |
+
bpy.ops.object.mode_set(mode='EDIT')
|
540 |
+
bpy.ops.mesh.remove_doubles(threshold=0.000001)
|
541 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
542 |
+
|
543 |
+
context.tool_settings.mesh_select_mode = pre_mesh_select_mode
|
544 |
+
if self.is_seam:
|
545 |
+
bpy.ops.object.mode_set(mode='EDIT')
|
546 |
+
bpy.ops.mesh.select_all(action='SELECT')
|
547 |
+
bpy.ops.uv.seams_from_islands()
|
548 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
549 |
+
bpy.ops.object.mode_set(mode='EDIT')
|
550 |
+
bpy.ops.mesh.select_all(action='DESELECT')
|
551 |
+
bpy.ops.object.mode_set(mode='OBJECT')
|
552 |
+
|
553 |
+
if self.is_armature:
|
554 |
+
mod = ob.modifiers.new("Armature", 'ARMATURE')
|
555 |
+
mod.object = arm_ob
|
556 |
+
context.scene.objects.active = arm_ob
|
557 |
+
bpy.ops.object.parent_set(type='OBJECT', keep_transform=True)
|
558 |
+
context.scene.objects.active = ob
|
559 |
+
context.window_manager.progress_update(8)
|
560 |
+
|
561 |
+
# マテリアル情報のテキスト埋め込み
|
562 |
+
if self.is_mate_data_text:
|
563 |
+
for index, data in enumerate(material_data):
|
564 |
+
txt_name = "Material:" + str(index)
|
565 |
+
if txt_name in context.blend_data.texts:
|
566 |
+
txt = context.blend_data.texts[txt_name]
|
567 |
+
txt.clear()
|
568 |
+
else:
|
569 |
+
txt = context.blend_data.texts.new(txt_name)
|
570 |
+
txt.write("1000" + "\n")
|
571 |
+
txt.write(data['name1'].lower() + "\n")
|
572 |
+
txt.write(data['name1'] + "\n")
|
573 |
+
txt.write(data['name2'] + "\n")
|
574 |
+
txt.write(data['name3'] + "\n")
|
575 |
+
txt.write("\n")
|
576 |
+
for tex_data in data['data']:
|
577 |
+
txt.write(tex_data['type'] + "\n")
|
578 |
+
if tex_data['type'] == 'tex':
|
579 |
+
txt.write("\t" + tex_data['name'] + "\n")
|
580 |
+
txt.write("\t" + tex_data['type2'] + "\n")
|
581 |
+
if tex_data['type2'] == 'tex2d':
|
582 |
+
txt.write("\t" + tex_data['name2'] + "\n")
|
583 |
+
txt.write("\t" + tex_data['path'] + "\n")
|
584 |
+
col = " ".join([str(tex_data['color'][0]), str(tex_data['color'][1]), str(tex_data['color'][2]), str(tex_data['color'][3])])
|
585 |
+
txt.write("\t" + col + "\n")
|
586 |
+
elif tex_data['type'] == 'col':
|
587 |
+
txt.write("\t" + tex_data['name'] + "\n")
|
588 |
+
col = " ".join([str(tex_data['color'][0]), str(tex_data['color'][1]), str(tex_data['color'][2]), str(tex_data['color'][3])])
|
589 |
+
txt.write("\t" + col + "\n")
|
590 |
+
elif tex_data['type'] == 'f':
|
591 |
+
txt.write("\t" + tex_data['name'] + "\n")
|
592 |
+
txt.write("\t" + str(tex_data['float']) + "\n")
|
593 |
+
txt.current_line_index = 0
|
594 |
+
context.window_manager.progress_update(9)
|
595 |
+
|
596 |
+
# ボーン情報のテキスト埋め込み
|
597 |
+
if self.is_bone_data_text:
|
598 |
+
if "BoneData" in context.blend_data.texts:
|
599 |
+
txt = context.blend_data.texts["BoneData"]
|
600 |
+
txt.clear()
|
601 |
+
else:
|
602 |
+
txt = context.blend_data.texts.new("BoneData")
|
603 |
+
for i, data in enumerate(bone_data):
|
604 |
+
s = ",".join([data['name'], str(data['unknown']), ""])
|
605 |
+
parent_index = data['parent_index']
|
606 |
+
if -1 < parent_index:
|
607 |
+
s += bone_data[parent_index]['name'] + ","
|
608 |
+
else:
|
609 |
+
s += "None" + ","
|
610 |
+
s += " ".join([str(data['co'][0]), str(data['co'][1]), str(data['co'][2])]) + ","
|
611 |
+
s += " ".join([str(data['rot'][0]), str(data['rot'][1]), str(data['rot'][2]), str(data['rot'][3])])
|
612 |
+
|
613 |
+
if self.is_bone_data_text:
|
614 |
+
txt.write(s + "\n")
|
615 |
+
if self.is_mesh and self.is_bone_data_obj_property:
|
616 |
+
ob["BoneData:" + str(i)] = s
|
617 |
+
if self.is_armature and self.is_bone_data_arm_property:
|
618 |
+
arm["BoneData:" + str(i)] = s
|
619 |
+
if self.is_bone_data_text:
|
620 |
+
txt['BaseBone'] = model_name2
|
621 |
+
txt.current_line_index = 0
|
622 |
+
context.window_manager.progress_update(10)
|
623 |
+
|
624 |
+
# ローカルボーン情報のテキスト埋め込み
|
625 |
+
if self.is_bone_data_text:
|
626 |
+
if "LocalBoneData" in context.blend_data.texts:
|
627 |
+
txt = context.blend_data.texts["LocalBoneData"]
|
628 |
+
txt.clear()
|
629 |
+
else:
|
630 |
+
txt = context.blend_data.texts.new("LocalBoneData")
|
631 |
+
for i, data in enumerate(local_bone_data):
|
632 |
+
s = data['name'] + ","
|
633 |
+
|
634 |
+
mat_list = list(data['matrix'][0])
|
635 |
+
mat_list.extend(list(data['matrix'][1]))
|
636 |
+
mat_list.extend(list(data['matrix'][2]))
|
637 |
+
mat_list.extend(list(data['matrix'][3]))
|
638 |
+
for j, f in enumerate(mat_list):
|
639 |
+
mat_list[j] = str(f)
|
640 |
+
s += " ".join(mat_list)
|
641 |
+
|
642 |
+
if self.is_bone_data_text:
|
643 |
+
txt.write(s + "\n")
|
644 |
+
if self.is_mesh and self.is_bone_data_obj_property:
|
645 |
+
ob["LocalBoneData:" + str(i)] = s
|
646 |
+
if self.is_armature and self.is_bone_data_arm_property:
|
647 |
+
arm["LocalBoneData:" + str(i)] = s
|
648 |
+
if self.is_bone_data_text:
|
649 |
+
txt['BaseBone'] = model_name2
|
650 |
+
txt.current_line_index = 0
|
651 |
+
|
652 |
+
if self.is_mesh and self.is_bone_data_obj_property:
|
653 |
+
ob['BaseBone'] = model_name2
|
654 |
+
if self.is_armature and self.is_bone_data_arm_property:
|
655 |
+
arm['BaseBone'] = model_name2
|
656 |
+
context.window_manager.progress_end()
|
657 |
+
|
658 |
+
require_time_str = str(round(time.time() - start_time, 1))
|
659 |
+
filesize = os.path.getsize(self.filepath)
|
660 |
+
filesize_str = str(filesize) + " バイト"
|
661 |
+
if 1024 * 1024 < filesize:
|
662 |
+
filesize_str = str(round(filesize / (1024 * 1024.0), 1)) + " MB"
|
663 |
+
elif 1024 < filesize:
|
664 |
+
filesize_str = str(round(filesize / 1024.0, 1)) + " KB"
|
665 |
+
self.report(type={'INFO'}, message="Model Was Imported Successfuly (" + filesize_str + " / " + require_time_str + " Seconds)")
|
666 |
+
|
667 |
+
return {'FINISHED'}
|
668 |
+
|
669 |
+
# メニューを登録する関数
|
670 |
+
def menu_func(self, context):
|
671 |
+
self.layout.operator(import_cm3d2_model.bl_idname, icon_value=common.preview_collections['main']['KISS'].icon_id)
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/tex_export.py
ADDED
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import bpy
|
2 |
+
import os
|
3 |
+
import struct
|
4 |
+
from . import common
|
5 |
+
|
6 |
+
class export_cm3d2_tex(bpy.types.Operator):
|
7 |
+
bl_idname = 'image.export_cm3d2_tex'
|
8 |
+
bl_label = "Save As .tex"
|
9 |
+
bl_description = "Current image will be saved as a (.tex)."
|
10 |
+
bl_options = {'REGISTER'}
|
11 |
+
|
12 |
+
filepath = bpy.props.StringProperty(subtype='FILE_PATH')
|
13 |
+
filename_ext = ".tex"
|
14 |
+
filter_glob = bpy.props.StringProperty(default="*.tex", options={'HIDDEN'})
|
15 |
+
|
16 |
+
is_backup = bpy.props.BoolProperty(name="Backup", default=True, description="Overwritten files will be backed up.")
|
17 |
+
|
18 |
+
version = bpy.props.IntProperty(name="Version", default=1000, min=1000, max=1111, soft_min=1000, soft_max=1111, step=1)
|
19 |
+
path = bpy.props.StringProperty(name="Path", default="assets/texture/texture/*.png")
|
20 |
+
|
21 |
+
@classmethod
|
22 |
+
def poll(cls, context):
|
23 |
+
img = context.edit_image
|
24 |
+
if img:
|
25 |
+
if len(img.pixels) or img.source == 'VIEWER':
|
26 |
+
return True
|
27 |
+
return False
|
28 |
+
|
29 |
+
def invoke(self, context, event):
|
30 |
+
img = context.edit_image
|
31 |
+
if img.filepath:
|
32 |
+
common.preferences().tex_export_path = bpy.path.abspath(img.filepath)
|
33 |
+
if common.preferences().tex_default_path:
|
34 |
+
self.filepath = common.default_cm3d2_dir(common.preferences().tex_default_path, common.remove_serial_number(img.name), "tex")
|
35 |
+
else:
|
36 |
+
self.filepath = common.default_cm3d2_dir(common.preferences().tex_export_path, common.remove_serial_number(img.name), "tex")
|
37 |
+
self.is_backup = bool(common.preferences().backup_ext)
|
38 |
+
if 'cm3d2_path' in img:
|
39 |
+
self.path = img['cm3d2_path']
|
40 |
+
else:
|
41 |
+
self.path = "assets/texture/texture/" + os.path.basename(self.filepath)
|
42 |
+
if 'tex Name' in img:
|
43 |
+
self.filepath = os.path.join(os.path.dirname(self.filepath), img['tex Name'])
|
44 |
+
context.window_manager.fileselect_add(self)
|
45 |
+
return {'RUNNING_MODAL'}
|
46 |
+
|
47 |
+
def draw(self, context):
|
48 |
+
row = self.layout.row()
|
49 |
+
row.prop(self, 'is_backup', icon='FILE_BACKUP')
|
50 |
+
if not common.preferences().backup_ext:
|
51 |
+
row.enabled = False
|
52 |
+
self.layout.prop(self, 'version', icon='LINENUMBERS_ON')
|
53 |
+
self.layout.prop(self, 'path', icon='ANIM')
|
54 |
+
|
55 |
+
def execute(self, context):
|
56 |
+
common.preferences().tex_export_path = self.filepath
|
57 |
+
|
58 |
+
try:
|
59 |
+
file = common.open_temporary(self.filepath, 'wb', is_backup=self.is_backup)
|
60 |
+
except:
|
61 |
+
self.report(type={'ERROR'}, message="Failed to save file, possibly inaccessible.")
|
62 |
+
return {'CANCELLED'}
|
63 |
+
|
64 |
+
try:
|
65 |
+
with file:
|
66 |
+
self.write_texture(context, file)
|
67 |
+
except common.CM3D2ExportException as e:
|
68 |
+
self.report(type={'ERROR'}, message=str(e))
|
69 |
+
return {'CANCELLED'}
|
70 |
+
|
71 |
+
return {'FINISHED'}
|
72 |
+
|
73 |
+
def write_texture(self, context, file):
|
74 |
+
# とりあえずpngで保存
|
75 |
+
img = context.edit_image
|
76 |
+
if img.source != 'VIEWER':
|
77 |
+
temp_path = self.filepath + ".temp.png"
|
78 |
+
else:
|
79 |
+
temp_path = os.path.splitext(self.filepath)[0] + ".png"
|
80 |
+
pre_filepath = bpy.path.abspath(img.filepath)
|
81 |
+
pre_source = img.source
|
82 |
+
override = context.copy()
|
83 |
+
override['edit_image'] = img
|
84 |
+
try:
|
85 |
+
save_as_render = True if pre_source == 'VIEWER' else False
|
86 |
+
copy = True if pre_source == 'VIEWER' else False
|
87 |
+
bpy.ops.image.save_as(override, save_as_render=save_as_render, copy=copy, filepath=temp_path, relative_path=True, show_multiview=False, use_multiview=False)
|
88 |
+
is_remove = True
|
89 |
+
except:
|
90 |
+
if os.path.exists( bpy.path.abspath(img.filepath) ):
|
91 |
+
temp_path = bpy.path.abspath(img.filepath)
|
92 |
+
is_remove = False
|
93 |
+
else:
|
94 |
+
raise common.CM3D2ExportException("Could not export .tex file.")
|
95 |
+
if pre_source != 'VIEWER':
|
96 |
+
img.filepath = pre_filepath
|
97 |
+
img.source = pre_source
|
98 |
+
|
99 |
+
# pngバイナリを全て読み込み
|
100 |
+
with open(temp_path, 'rb') as temp_file:
|
101 |
+
temp_data = temp_file.read()
|
102 |
+
# 一時ファイルを削除
|
103 |
+
if is_remove:
|
104 |
+
os.remove(temp_path)
|
105 |
+
|
106 |
+
# 本命ファイルに書き込み
|
107 |
+
common.write_str(file, 'CM3D2_TEX')
|
108 |
+
file.write(struct.pack('<i', self.version))
|
109 |
+
common.write_str(file, self.path)
|
110 |
+
file.write(struct.pack('<i', len(temp_data)))
|
111 |
+
file.write(temp_data)
|
112 |
+
|
113 |
+
# メニューを登録する関数
|
114 |
+
def menu_func(self, context):
|
115 |
+
self.layout.operator(export_cm3d2_tex.bl_idname, icon_value=common.preview_collections['main']['KISS'].icon_id)
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Converter (English)/tex_import.py
ADDED
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os, os.path, bpy, struct, os.path
|
2 |
+
from . import common
|
3 |
+
|
4 |
+
class import_cm3d2_tex(bpy.types.Operator):
|
5 |
+
bl_idname = 'image.import_cm3d2_tex'
|
6 |
+
bl_label = "Import .tex"
|
7 |
+
bl_description = "Imports a CM3D2 tex file (.tex)"
|
8 |
+
bl_options = {'REGISTER'}
|
9 |
+
|
10 |
+
filepath = bpy.props.StringProperty(subtype='FILE_PATH')
|
11 |
+
filename_ext = ".tex;.png"
|
12 |
+
filter_glob = bpy.props.StringProperty(default="*.tex;*.png", options={'HIDDEN'})
|
13 |
+
|
14 |
+
items = [
|
15 |
+
('PACK', "Package", "", 'PACKAGE', 1),
|
16 |
+
('PNG', "Opens or converts to png", "", 'IMAGE_DATA', 2),
|
17 |
+
]
|
18 |
+
mode = bpy.props.EnumProperty(items=items, name="Mode", default='PNG')
|
19 |
+
|
20 |
+
def invoke(self, context, event):
|
21 |
+
if common.preferences().tex_default_path:
|
22 |
+
self.filepath = common.default_cm3d2_dir(common.preferences().tex_default_path, "", "tex")
|
23 |
+
else:
|
24 |
+
self.filepath = common.default_cm3d2_dir(common.preferences().tex_import_path, "", "tex")
|
25 |
+
context.window_manager.fileselect_add(self)
|
26 |
+
return {'RUNNING_MODAL'}
|
27 |
+
|
28 |
+
def draw(self, context):
|
29 |
+
box = self.layout.box()
|
30 |
+
col = box.column(align=True)
|
31 |
+
col.label(text="Mode", icon='FILESEL')
|
32 |
+
col.prop(self, 'mode', icon='FILESEL', expand=True)
|
33 |
+
|
34 |
+
def execute(self, context):
|
35 |
+
common.preferences().tex_import_path = self.filepath
|
36 |
+
try:
|
37 |
+
file = open(self.filepath, 'rb')
|
38 |
+
except:
|
39 |
+
self.report(type={'ERROR'}, message="Failed to open the file, it does not exist or is inaccessible")
|
40 |
+
return {'CANCELLED'}
|
41 |
+
header_ext = common.read_str(file)
|
42 |
+
if header_ext == 'CM3D2_TEX':
|
43 |
+
file.seek(4, 1)
|
44 |
+
in_path = common.read_str(file)
|
45 |
+
png_size = struct.unpack('<i', file.read(4))[0]
|
46 |
+
root, ext = os.path.splitext(self.filepath)
|
47 |
+
png_path = root + ".png"
|
48 |
+
is_png_overwrite = os.path.exists(png_path)
|
49 |
+
if self.mode == 'PACK' and is_png_overwrite:
|
50 |
+
png_path += ".temp.png"
|
51 |
+
png_file = open(png_path, 'wb')
|
52 |
+
png_file.write(file.read(png_size))
|
53 |
+
png_file.close()
|
54 |
+
bpy.ops.image.open(filepath=png_path)
|
55 |
+
img = context.edit_image
|
56 |
+
img.name = os.path.basename(self.filepath)
|
57 |
+
img['cm3d2_path'] = in_path
|
58 |
+
else:
|
59 |
+
bpy.ops.image.open(filepath=self.filepath)
|
60 |
+
img = context.edit_image
|
61 |
+
file.close()
|
62 |
+
if self.mode == 'PACK':
|
63 |
+
img.pack(as_png=True)
|
64 |
+
if header_ext == 'CM3D2_TEX':
|
65 |
+
os.remove(png_path)
|
66 |
+
return {'FINISHED'}
|
67 |
+
|
68 |
+
# メニューを登録する関数
|
69 |
+
def menu_func(self, context):
|
70 |
+
self.layout.separator()
|
71 |
+
self.layout.operator(import_cm3d2_tex.bl_idname, icon_value=common.preview_collections['main']['KISS'].icon_id)
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Mod Search (English)/CM3D2 Mod Search.exe
ADDED
Binary file (88.6 kB). View file
|
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Mod Search (English)/CM3D2 Mod Search.html
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<html>
|
2 |
+
<head>
|
3 |
+
<meta charset="utf-8">
|
4 |
+
<title>CM3D2 Mod Search 「noire」Search Results</title>
|
5 |
+
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
|
6 |
+
</head>
|
7 |
+
<body>
|
8 |
+
<table cellspacing="0" class="table table-small-font table-hover">
|
9 |
+
<thead>
|
10 |
+
<tr bgcolor=#ffc692>
|
11 |
+
<th><span class="glyphicon glyphicon-file" aria-hidden="true"></span>ファイル</th>
|
12 |
+
<th>コメント</th>
|
13 |
+
<th data-priority="5">オリジナル</th>
|
14 |
+
<th data-priority="4">容量</th>
|
15 |
+
<th data-priority="3">日時</th>
|
16 |
+
<th><span class="glyphicon glyphicon-cloud-download" aria-hidden="true"></span></th>
|
17 |
+
<th> <span class="glyphicon glyphicon-edit" aria-hidden="true"></span></th>
|
18 |
+
</tr>
|
19 |
+
</thead>
|
20 |
+
</table>
|
21 |
+
</body>
|
22 |
+
</html>
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Mod Search (English)/CM3D2 Mod Search.ini
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[Settings]
|
2 |
+
URLs=http://ux.getuploader.com/cm3d2/,http://ux.getuploader.com/cm3d2_b/,http://ux.getuploader.com/cm3d2_c/,http://ux.getuploader.com/cm3d2_d/,http://ux.getuploader.com/cm3d2_e/,http://ux.getuploader.com/cm3d2_f/,http://ux.getuploader.com/cm3d2_g/
|
3 |
+
RecentSearchWords=noire,
|
4 |
+
[Window]
|
5 |
+
X=100
|
6 |
+
Y=100
|
7 |
+
W=800
|
8 |
+
H=600
|
9 |
+
State=Normal
|
vae/com3d/[CM3D2]English Mod Tools Pack/CM3D2 Mod Search (English)/Readme & Modder List.txt
ADDED
@@ -0,0 +1,213 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
This neat little tool will quickly search any inserted uploaders for whatever keywords you type in.
|
2 |
+
it will then show you the results in a neat and concise list.
|
3 |
+
|
4 |
+
Below are a bunch of modder sites. Feel free to add these to the search list inside of the app.
|
5 |
+
|
6 |
+
I take no credit for the creation of this app and only for it's translation.
|
7 |
+
|
8 |
+
|
9 |
+
http://ux.getuploader.com/cm3d2/
|
10 |
+
http://ux.getuploader.com/cm3d2_b/
|
11 |
+
http://ux.getuploader.com/cm3d2_c/
|
12 |
+
http://ux.getuploader.com/cm3d2_d/
|
13 |
+
http://ux.getuploader.com/cm3d2_e/
|
14 |
+
http://ux.getuploader.com/cm3d2_f/
|
15 |
+
http://ux.getuploader.com/cm3d2_g/
|
16 |
+
|
17 |
+
http://ux.getuploader.com/072pro_staff/
|
18 |
+
http://ux.getuploader.com/24674/
|
19 |
+
http://ux.getuploader.com/2501b/
|
20 |
+
http://ux.getuploader.com/99lI_MOD/
|
21 |
+
http://ux.getuploader.com/Akatsuki_MOD/
|
22 |
+
http://ux.getuploader.com/AllCancel/
|
23 |
+
http://ux.getuploader.com/Amaterasunyan/
|
24 |
+
http://ux.getuploader.com/Brandish_mod/
|
25 |
+
http://ux.getuploader.com/CANTUSandNATURE_01/
|
26 |
+
http://ux.getuploader.com/CM3D2MODhanger/
|
27 |
+
http://ux.getuploader.com/CM3D2_a901/
|
28 |
+
http://ux.getuploader.com/CM3D2_fyi/
|
29 |
+
http://ux.getuploader.com/CM3D2mod_08860/
|
30 |
+
http://ux.getuploader.com/CMsande_tu_/
|
31 |
+
http://ux.getuploader.com/C_take/
|
32 |
+
http://ux.getuploader.com/DV_CM3D2_A/
|
33 |
+
http://ux.getuploader.com/Eastpoint/
|
34 |
+
http://ux.getuploader.com/Extra_B/
|
35 |
+
http://ux.getuploader.com/FestaSia_cm3d2/
|
36 |
+
http://ux.getuploader.com/GentlemenX/
|
37 |
+
http://ux.getuploader.com/HEXAworks/
|
38 |
+
http://ux.getuploader.com/IBE/
|
39 |
+
http://ux.getuploader.com/KAAN/
|
40 |
+
http://ux.getuploader.com/Kikka_Sora/
|
41 |
+
http://ux.getuploader.com/LN_preset/
|
42 |
+
http://ux.getuploader.com/LaiNC519/
|
43 |
+
http://ux.getuploader.com/Lan/
|
44 |
+
http://ux.getuploader.com/MISTERJUICE/
|
45 |
+
http://ux.getuploader.com/Merchantscache_/
|
46 |
+
http://ux.getuploader.com/NekohigeLoda/
|
47 |
+
http://ux.getuploader.com/NewSportsX/
|
48 |
+
http://ux.getuploader.com/P90X/
|
49 |
+
http://ux.getuploader.com/Permafrost_mod_Refrigerator/
|
50 |
+
http://ux.getuploader.com/Picomagic/
|
51 |
+
http://ux.getuploader.com/Pot_lemon/
|
52 |
+
http://ux.getuploader.com/SDK251/
|
53 |
+
http://ux.getuploader.com/SDK251_b/
|
54 |
+
http://ux.getuploader.com/Satoshi_HiMEz_CM3D2MOD/
|
55 |
+
http://ux.getuploader.com/Stuk4Mephisto/
|
56 |
+
http://ux.getuploader.com/Su_WH/
|
57 |
+
http://ux.getuploader.com/Summer_flights/
|
58 |
+
http://ux.getuploader.com/Taker_3D/
|
59 |
+
http://ux.getuploader.com/YXQ/
|
60 |
+
http://ux.getuploader.com/YYY333/
|
61 |
+
http://ux.getuploader.com/YaYeah/
|
62 |
+
http://ux.getuploader.com/acer1234040/
|
63 |
+
http://ux.getuploader.com/acer1234040smod/
|
64 |
+
http://ux.getuploader.com/actA_M10/
|
65 |
+
http://ux.getuploader.com/ajicalipton/
|
66 |
+
http://ux.getuploader.com/akabane99/
|
67 |
+
http://ux.getuploader.com/akathuki01/
|
68 |
+
http://ux.getuploader.com/akau4590/
|
69 |
+
http://ux.getuploader.com/amb_cm3d2/
|
70 |
+
http://ux.getuploader.com/amecloud/
|
71 |
+
http://ux.getuploader.com/ash_cm3d2/
|
72 |
+
http://ux.getuploader.com/blade_ero/
|
73 |
+
http://ux.getuploader.com/butagoya_s/
|
74 |
+
http://ux.getuploader.com/cgmuploader/
|
75 |
+
http://ux.getuploader.com/cm3d2_Wellvin/
|
76 |
+
http://ux.getuploader.com/cm3d2_glossmod/
|
77 |
+
http://ux.getuploader.com/cm3d2_helmi/
|
78 |
+
http://ux.getuploader.com/cm3d2_marderleone/
|
79 |
+
http://ux.getuploader.com/cm3d2_okm/
|
80 |
+
http://ux.getuploader.com/cm3d2_snow1/
|
81 |
+
http://ux.getuploader.com/cm3d2mod_usac/
|
82 |
+
http://ux.getuploader.com/cm3d_fake/
|
83 |
+
http://ux.getuploader.com/cmzmrrd/
|
84 |
+
http://ux.getuploader.com/customP/
|
85 |
+
http://ux.getuploader.com/cycy_harmony/
|
86 |
+
http://ux.getuploader.com/drago_00/
|
87 |
+
http://ux.getuploader.com/eclipsexxx/
|
88 |
+
http://ux.getuploader.com/econo_cm3d2/
|
89 |
+
http://ux.getuploader.com/eden_bakamaid/
|
90 |
+
http://ux.getuploader.com/eixaregood/
|
91 |
+
http://ux.getuploader.com/errafile001/
|
92 |
+
http://ux.getuploader.com/es45as/
|
93 |
+
http://ux.getuploader.com/exenrar/
|
94 |
+
http://ux.getuploader.com/fatelok/
|
95 |
+
http://ux.getuploader.com/fel666/
|
96 |
+
http://ux.getuploader.com/fenz_cm3d2/
|
97 |
+
http://ux.getuploader.com/fr1ed_cm3d2mod/
|
98 |
+
http://ux.getuploader.com/futanarichinko/
|
99 |
+
http://ux.getuploader.com/galleBlizzg/
|
100 |
+
http://ux.getuploader.com/gramcm3d2/
|
101 |
+
http://ux.getuploader.com/gucci3_a/
|
102 |
+
http://ux.getuploader.com/hads/
|
103 |
+
http://ux.getuploader.com/hagesaki_mod/
|
104 |
+
http://ux.getuploader.com/hashiokiva/
|
105 |
+
http://ux.getuploader.com/hatena37/
|
106 |
+
http://ux.getuploader.com/hexbox/
|
107 |
+
http://ux.getuploader.com/himuro35/
|
108 |
+
http://ux.getuploader.com/homuhomudkawaii/
|
109 |
+
http://ux.getuploader.com/honoka391/
|
110 |
+
http://ux.getuploader.com/hukami_souka/
|
111 |
+
http://ux.getuploader.com/ianman1110/
|
112 |
+
http://ux.getuploader.com/insight_MOD/
|
113 |
+
http://ux.getuploader.com/jmikam/
|
114 |
+
http://ux.getuploader.com/kafil/
|
115 |
+
http://ux.getuploader.com/kamonan/
|
116 |
+
http://ux.getuploader.com/kazami/
|
117 |
+
http://ux.getuploader.com/kensancmmodup/
|
118 |
+
http://ux.getuploader.com/kim841213/
|
119 |
+
http://ux.getuploader.com/kirisima_presets/
|
120 |
+
http://ux.getuploader.com/kk_loader/
|
121 |
+
http://ux.getuploader.com/kura_gee/
|
122 |
+
http://ux.getuploader.com/kuroto_CM3D2/
|
123 |
+
http://ux.getuploader.com/liktomaid/
|
124 |
+
http://ux.getuploader.com/lithla/
|
125 |
+
http://ux.getuploader.com/maid_yasen/
|
126 |
+
http://ux.getuploader.com/maidinnet_01/
|
127 |
+
http://ux.getuploader.com/makaroninoana/
|
128 |
+
http://ux.getuploader.com/manbo_uploader/
|
129 |
+
http://ux.getuploader.com/maro_hato/
|
130 |
+
http://ux.getuploader.com/masarun001/
|
131 |
+
http://ux.getuploader.com/maumauhanna/
|
132 |
+
http://ux.getuploader.com/melala001/
|
133 |
+
http://ux.getuploader.com/mikan07/
|
134 |
+
http://ux.getuploader.com/mikenuko_uproda/
|
135 |
+
http://ux.getuploader.com/mikenuko_uproda02/
|
136 |
+
http://ux.getuploader.com/milky_mh_craft/
|
137 |
+
http://ux.getuploader.com/minami_cm3d2/
|
138 |
+
http://ux.getuploader.com/minittogoma/
|
139 |
+
http://ux.getuploader.com/mmm_CM3D2_666/
|
140 |
+
http://ux.getuploader.com/modcm3d2izk/
|
141 |
+
http://ux.getuploader.com/mogga13/
|
142 |
+
http://ux.getuploader.com/monotoneclub/
|
143 |
+
http://ux.getuploader.com/motto/
|
144 |
+
http://ux.getuploader.com/my_cm3d2_up/
|
145 |
+
http://ux.getuploader.com/mycm3d2mod/
|
146 |
+
http://ux.getuploader.com/n777_mod/
|
147 |
+
http://ux.getuploader.com/nabeochang/
|
148 |
+
http://ux.getuploader.com/nabetajin_dress/
|
149 |
+
http://ux.getuploader.com/nashiro355/
|
150 |
+
http://ux.getuploader.com/nekoko_cm3d2_mod/
|
151 |
+
http://ux.getuploader.com/nicocchi25/
|
152 |
+
http://ux.getuploader.com/nobelchoco/
|
153 |
+
http://ux.getuploader.com/nomorehelpaa/
|
154 |
+
http://ux.getuploader.com/nora151104/
|
155 |
+
http://ux.getuploader.com/nose_i_xxx_CM3D2/
|
156 |
+
http://ux.getuploader.com/opqrstu/
|
157 |
+
http://ux.getuploader.com/owl_no_uploader/
|
158 |
+
http://ux.getuploader.com/polaris001/
|
159 |
+
http://ux.getuploader.com/ponyori/
|
160 |
+
http://ux.getuploader.com/popopo_cm3d2/
|
161 |
+
http://ux.getuploader.com/purioji/
|
162 |
+
http://ux.getuploader.com/qqyp/
|
163 |
+
http://ux.getuploader.com/queserasera30/
|
164 |
+
http://ux.getuploader.com/quick_quit_001/
|
165 |
+
http://ux.getuploader.com/real0311/
|
166 |
+
http://ux.getuploader.com/reaper7092/
|
167 |
+
http://ux.getuploader.com/reason4/
|
168 |
+
http://ux.getuploader.com/reisen3d2/
|
169 |
+
http://ux.getuploader.com/reynnoapuroda_cm3d2/
|
170 |
+
http://ux.getuploader.com/ro_cm3d2/
|
171 |
+
http://ux.getuploader.com/route163/
|
172 |
+
http://ux.getuploader.com/ryow_GX_up/
|
173 |
+
http://ux.getuploader.com/saidenka_cm3d2/
|
174 |
+
http://ux.getuploader.com/salvia_cm3d2/
|
175 |
+
http://ux.getuploader.com/sangarianmodcm3d2/
|
176 |
+
http://ux.getuploader.com/saphir/
|
177 |
+
http://ux.getuploader.com/sasagami/
|
178 |
+
http://ux.getuploader.com/satyam/
|
179 |
+
http://ux.getuploader.com/sekayuri/
|
180 |
+
http://ux.getuploader.com/settun_kossori/
|
181 |
+
http://ux.getuploader.com/siromiso/
|
182 |
+
http://ux.getuploader.com/skfc_43/
|
183 |
+
http://ux.getuploader.com/squids/
|
184 |
+
http://ux.getuploader.com/suikoron/
|
185 |
+
http://ux.getuploader.com/syano_mod/
|
186 |
+
http://ux.getuploader.com/takahashiren/
|
187 |
+
http://ux.getuploader.com/tarutarutaru/
|
188 |
+
http://ux.getuploader.com/ten4chan/
|
189 |
+
http://ux.getuploader.com/test_20160728/
|
190 |
+
http://ux.getuploader.com/tfa404953b/
|
191 |
+
http://ux.getuploader.com/thethe/
|
192 |
+
http://ux.getuploader.com/tk_CM3D2/
|
193 |
+
http://ux.getuploader.com/tokinagare/
|
194 |
+
http://ux.getuploader.com/toshizi0001/
|
195 |
+
http://ux.getuploader.com/trzr_tool/
|
196 |
+
http://ux.getuploader.com/umiushi555/
|
197 |
+
http://ux.getuploader.com/unyohonyoho/
|
198 |
+
http://ux.getuploader.com/uproda114514/
|
199 |
+
http://ux.getuploader.com/wahyu_azunyan/
|
200 |
+
http://ux.getuploader.com/wakewakame/
|
201 |
+
http://ux.getuploader.com/wasabi_mod/
|
202 |
+
http://ux.getuploader.com/webhaikyoyoteichi/
|
203 |
+
http://ux.getuploader.com/wei/
|
204 |
+
http://ux.getuploader.com/wjccc/
|
205 |
+
http://ux.getuploader.com/x5993279/
|
206 |
+
http://ux.getuploader.com/xxHiryuxx/
|
207 |
+
http://ux.getuploader.com/yakinD/
|
208 |
+
http://ux.getuploader.com/yamadatarou3/
|
209 |
+
http://ux.getuploader.com/yuizero/
|
210 |
+
http://ux.getuploader.com/yuki_okiba/
|
211 |
+
http://ux.getuploader.com/yuunagimizuki/
|
212 |
+
http://ux.getuploader.com/z5517356/
|
213 |
+
http://ux.getuploader.com/zerogm10/
|
vae/com3d/[CM3D2]English Mod Tools Pack/MenuEdit2017420/Dictionary and How to.txt
ADDED
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
General:
|
2 |
+
File Version = If there are two or more of the same .menu, the one with the highest number will be loaded
|
3 |
+
Name = Name of the item (Maximal Number of Characters TBD) Do not edit!
|
4 |
+
Describe = Description of the item (Maximal Number of Characters TBD, Maximal Number of Lines: 3?) Do not edit!
|
5 |
+
For Copy and Paste = Use �s���s�t to start a new line in the description
|
6 |
+
txt path = path to the txt (need more info)
|
7 |
+
���j���[�t�H���_ = Menu folder, there are DRESS... (and BODY? need more info)
|
8 |
+
category = Category in which the Menu Icon will appear (Reference List at the Bottom)
|
9 |
+
catno = ? (category number? where it will be placed in the category?
|
10 |
+
�����lj� = Attribute added? Known attributes in the reference list
|
11 |
+
priority = priority will state where the icon will appear. the lower the priority the higher in the list the mod appears.
|
12 |
+
name = Name of the item (Maximal Number of Characters TBD)
|
13 |
+
setumei = Description of the item (Maximal Number of Characters TBD, Maximal Number of Lines: 3?)
|
14 |
+
icons = Icon Texture for the Mod in .tex format
|
15 |
+
onclickmenu =
|
16 |
+
�A�C�e���p�����[�^ = Item paramters?
|
17 |
+
�A�C�e�� = Item (call additional .menu to e.g remove two of these three when selecting teh third: Top, Swimsuit, One-Piece)
|
18 |
+
additem = Model for the Mod in .model format (and category)
|
19 |
+
maskitem = Categories that will be hidden when this piece is visible, additionally to the ones in the reference list/replacing: Nipples have accNipL and accNipR for Left and Right
|
20 |
+
�}�e���A���ύX = Material change. Is useful for setting a quick Material swap.
|
21 |
+
���\�[�X�Q�� = Resource reference
|
22 |
+
node���� = node erase (Nodes erase or display certains parts of a maids body associated with the vertex group name.)
|
23 |
+
node�\�� = node display (Nodes erase or display certains parts of a maids body associated with the vertex group name.)
|
24 |
+
�A�C�e���ꎞ�I�ɓK�p = Items temporarily applied (Also used for removing?)
|
25 |
+
�J�X�� = Kasumi?
|
26 |
+
���� = if (cases)
|
27 |
+
�I�� = end
|
28 |
+
|
29 |
+
|
30 |
+
|
31 |
+
If cases:
|
32 |
+
�J�X����panz
|
33 |
+
�� = But
|
34 |
+
���� = Invalid
|
35 |
+
�Ȃ� = Nara?
|
36 |
+
|
37 |
+
|
38 |
+
Nodes:
|
39 |
+
Spine1
|
40 |
+
Spine1a
|
41 |
+
Uppertwist
|
42 |
+
Forearm
|
43 |
+
Foretwist
|
44 |
+
Hand
|
45 |
+
Mune
|
46 |
+
None
|
47 |
+
����node�ݒ�I�� = Erasure node setting end
|
48 |
+
�V�i���I�p�[�g�J�n = Scenario Part start
|
49 |
+
���h���X�A�b�v���ɃN���b�N���ꂽ���̏��� = �� processing when it is clicked in the dress-up
|
50 |
+
|
51 |
+
|
52 |
+
|
53 |
+
Attributes:
|
54 |
+
�������\�����Ȃ� = Do not show possession of the number of
|
55 |
+
|
56 |
+
Clothes:
|
57 |
+
--acchat = Hat
|
58 |
+
--head = Headdress
|
59 |
+
--wear = Top
|
60 |
+
--skrt = Bottom
|
61 |
+
--onep = One Piece
|
62 |
+
--mizugi = Swimsuit
|
63 |
+
--bra = Bra
|
64 |
+
--pants = Panties
|
65 |
+
--stkg = Socks
|
66 |
+
--shoe = Shoes
|
67 |
+
Accessory
|
68 |
+
-acckami = Front Hair
|
69 |
+
-megane = glasses
|
70 |
+
-acchead = Eye Mask
|
71 |
+
-acchana = Nose
|
72 |
+
-accmimi = Ears
|
73 |
+
-gloves = Gloves
|
74 |
+
-acckubi= Necklace
|
75 |
+
-acckubiwa = Choker
|
76 |
+
-acckamisub = Ribbon
|
77 |
+
-accnip = Nipples
|
78 |
+
-accude = Arms
|
79 |
+
-accesho = Belly
|
80 |
+
-accashi = Ankles
|
81 |
+
-accsenaka = Back
|
82 |
+
-accshippo = Tail
|
83 |
+
-accxxx = Genitals
|
84 |
+
Head
|
85 |
+
-face = Face
|
86 |
+
-eye_brow = Eyebrows
|
87 |
+
-eye = Eyes
|
88 |
+
-eye_hi = Highlights
|
89 |
+
-hokoro = Moles
|
90 |
+
-lip = Lips
|
91 |
+
-accha = Teeth
|
92 |
+
Hair
|
93 |
+
-hair_f = Front Hair
|
94 |
+
-hair_r = Back Hair
|
95 |
+
-hair_side = Side Hair
|
96 |
+
-hair_twin or hair_pony = Extensions
|
97 |
+
-hair_aho = Ahoge
|
98 |
+
Body
|
99 |
+
-skin = Skin
|
100 |
+
-chikubi = Nipples
|
101 |
+
-tatoo = Tattoos
|
102 |
+
-underhair = Pubic hair
|
vae/com3d/[CM3D2]English Mod Tools Pack/MenuEdit2017420/[CM3D2]menuEdit.exe
ADDED
Binary file (828 kB). View file
|
|
vae/com3d/[CM3D2]English Mod Tools Pack/MenuEdit2017420/readme.txt
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Original JP Version taken from cm3d2_d_84.
|
2 |
+
Translated and explained for easy international usage and newcomers by SakuraKoi with the help of the Hongfire Community.
|
3 |
+
|
4 |
+
-4/20/2017: Tiny edit by krypto5863
|
vae/com3d/[CM3D2]English Mod Tools Pack/MenuEdit2017420/src/[CM3D2]menuEdit.ahk
ADDED
@@ -0,0 +1,274 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#NoTrayIcon
|
2 |
+
FileEncoding, UTF-8-RAW
|
3 |
+
#SingleInstance OFF
|
4 |
+
SetFormat, float, 0.10
|
5 |
+
#NoTrayIcon
|
6 |
+
|
7 |
+
file_path = %1%
|
8 |
+
Loop {
|
9 |
+
if (file_path == "") {
|
10 |
+
default_path := GetDefaultPath()
|
11 |
+
FileSelectFile, file_path, 3, %default_path%, Select a .menu File, Menu File(*.menu)
|
12 |
+
}
|
13 |
+
if (ErrorLevel == 1) {
|
14 |
+
ExitApp
|
15 |
+
}
|
16 |
+
if (FileExist(file_path) == "") {
|
17 |
+
Msgbox, The file does not exist
|
18 |
+
file_path := ""
|
19 |
+
}
|
20 |
+
else {
|
21 |
+
break
|
22 |
+
}
|
23 |
+
}
|
24 |
+
file := FileOpen(file_path, "r")
|
25 |
+
if (file.Read(file.ReadChar()) != "CM3D2_MENU") {
|
26 |
+
MsgBox, This is not a menu file for Custom Maid 3D 2
|
27 |
+
ExitApp
|
28 |
+
}
|
29 |
+
|
30 |
+
data := Object()
|
31 |
+
|
32 |
+
version := file.ReadInt()
|
33 |
+
path := ReadString(file)
|
34 |
+
name := ReadString(file)
|
35 |
+
category := ReadString(file)
|
36 |
+
setumei := ReadString(file)
|
37 |
+
|
38 |
+
end_pos := file.ReadInt() + file.tell() - 1
|
39 |
+
Loop, 9999 {
|
40 |
+
index := A_Index
|
41 |
+
local_size := file.ReadChar()
|
42 |
+
Loop, %local_size% {
|
43 |
+
data[index, A_Index] := ReadString(file)
|
44 |
+
}
|
45 |
+
data_size := index
|
46 |
+
if (end_pos <= file.tell()) {
|
47 |
+
break
|
48 |
+
}
|
49 |
+
}
|
50 |
+
file.Close()
|
51 |
+
|
52 |
+
SplitPath, file_path, OutFileName, OutDir, OutExtension, OutNameNoExt, OutDrive
|
53 |
+
Gui, Add, Text, xm0 ym0 W400 H20 Center, %A_Space%%OutFileName%
|
54 |
+
Gui, Add, Text, x+0 yp+0 W100 H20 Center, For Copy and Paste
|
55 |
+
Gui, Add, Edit, x+0 yp+0 W125 H20 ReadOnly, 《改行》
|
56 |
+
|
57 |
+
Gui, Add, Text, xm0 y+0 W100 H20 Center, %A_Space%%A_Space%File Version
|
58 |
+
Gui, Add, Edit, x+0 yp+0 W100 H20 VGversion, %version%
|
59 |
+
Gui, Add, Text, x+0 yp+0 W75 H20 Center, %A_Space%%A_Space%Name
|
60 |
+
Gui, Add, Edit, x+0 yp+0 W350 H20 VGname, %name%
|
61 |
+
|
62 |
+
Gui, Add, Text, xm0 y+0 W100 H20 Center, %A_Space%%A_Space%Category
|
63 |
+
Gui, Add, Edit, x+0 yp+0 W100 H20 VGcategory, %category%
|
64 |
+
Gui, Add, Text, x+0 yp+0 W75 H20 Center, %A_Space%%A_Space%Description
|
65 |
+
Gui, Add, Edit, x+0 yp+0 W350 H20 VGsetumei, %setumei%
|
66 |
+
|
67 |
+
Gui, Add, Text, xm0 y+0 W100 H20 Center, %A_Space%%A_Space%txt path
|
68 |
+
Gui, Add, Edit, x+0 yp+0 W525 H20 VGpath, %path%
|
69 |
+
|
70 |
+
Gui, Add, TreeView, xm0 W625 R32 -ReadOnly
|
71 |
+
|
72 |
+
Gui, Add, Button, xm0 y+5 W100 H20 GUtilAdd, New Parent
|
73 |
+
Gui, Add, Button, x+0 yp+0 W100 H20 GUtilAddChild, New Child
|
74 |
+
Gui, Add, Button, x+325 yp+0 W100 H20 GUtilDel, Delete
|
75 |
+
|
76 |
+
Gui, Add, Button, xm0 y+5 W625 H50 GMySubmit, Save
|
77 |
+
|
78 |
+
Loop, %data_size% {
|
79 |
+
index := A_Index
|
80 |
+
id := TV_Add(data[index, 1], 0, "Expand")
|
81 |
+
local_size := data[index].MaxIndex()
|
82 |
+
Loop, %local_size% {
|
83 |
+
if (A_Index == 1) {
|
84 |
+
continue
|
85 |
+
}
|
86 |
+
TV_Add(data[index, A_Index], id)
|
87 |
+
}
|
88 |
+
}
|
89 |
+
|
90 |
+
Gui, Show, AutoSize
|
91 |
+
return
|
92 |
+
|
93 |
+
GetStringLength(string) {
|
94 |
+
count = 0
|
95 |
+
Loop, Parse, string
|
96 |
+
{
|
97 |
+
if (RegExMatch(A_LoopField, "^[^\x01-\x7E]$") != 0) {
|
98 |
+
count += 3
|
99 |
+
}
|
100 |
+
else {
|
101 |
+
count += 1
|
102 |
+
}
|
103 |
+
}
|
104 |
+
return count
|
105 |
+
}
|
106 |
+
|
107 |
+
ReadString(file, size=-1) {
|
108 |
+
if (size <= -1) {
|
109 |
+
size := 0
|
110 |
+
chars := Object()
|
111 |
+
Loop {
|
112 |
+
char := file.ReadUChar()
|
113 |
+
chars[A_Index] := char
|
114 |
+
if (char < 128) {
|
115 |
+
break
|
116 |
+
}
|
117 |
+
}
|
118 |
+
num := GetMaxIndex(chars)
|
119 |
+
Loop, %num% {
|
120 |
+
char := chars[A_Index]
|
121 |
+
multi := 256 ** (A_Index - 1)
|
122 |
+
size += char * multi
|
123 |
+
if (1 < A_Index) {
|
124 |
+
size -= (multi / 2) * (char + 1)
|
125 |
+
}
|
126 |
+
}
|
127 |
+
}
|
128 |
+
string := ""
|
129 |
+
count = 0
|
130 |
+
Loop, 9999 {
|
131 |
+
if (size <= count) {
|
132 |
+
break
|
133 |
+
}
|
134 |
+
s := file.Read(1)
|
135 |
+
string := string . s
|
136 |
+
count += GetStringLength(s)
|
137 |
+
if (GetStringLength(s) == 0) {
|
138 |
+
pos := file.Pos
|
139 |
+
MsgBox, Failed to read the file(place: %pos%)`n Quit
|
140 |
+
ExitApp
|
141 |
+
}
|
142 |
+
}
|
143 |
+
return string
|
144 |
+
}
|
145 |
+
|
146 |
+
WriteString(file, string) {
|
147 |
+
len := GetStringLength(string)
|
148 |
+
if (128 <= len) {
|
149 |
+
temp := Mod(len, 128) + 128
|
150 |
+
file.WriteChar(temp)
|
151 |
+
temp := Floor(len / 128)
|
152 |
+
file.WriteChar(temp)
|
153 |
+
}
|
154 |
+
else {
|
155 |
+
file.WriteChar(len)
|
156 |
+
}
|
157 |
+
file.Write(string)
|
158 |
+
}
|
159 |
+
|
160 |
+
GetMaxIndex(obj) {
|
161 |
+
value := obj.MaxIndex()
|
162 |
+
if (value == "") {
|
163 |
+
return 0
|
164 |
+
}
|
165 |
+
return value
|
166 |
+
}
|
167 |
+
|
168 |
+
GetDefaultPath() {
|
169 |
+
RegRead, path, HKEY_CURRENT_USER, Software\KISS\カスタムメイド3D2, InstallPath
|
170 |
+
if (ErrorLevel == 0) {
|
171 |
+
path = %path%GameData\
|
172 |
+
}
|
173 |
+
else {
|
174 |
+
path := A_ScriptDir
|
175 |
+
}
|
176 |
+
return path
|
177 |
+
}
|
178 |
+
|
179 |
+
UtilAdd:
|
180 |
+
InputBox, new_name, , Please enter the name of the new parent
|
181 |
+
if (ErrorLevel == 0){
|
182 |
+
TV_Add(new_name, 0, "Expand")
|
183 |
+
}
|
184 |
+
return
|
185 |
+
UtilAddChild:
|
186 |
+
parent := TV_GetSelection()
|
187 |
+
if (parent == 0) {
|
188 |
+
MsgBox, Please select an item
|
189 |
+
return
|
190 |
+
}
|
191 |
+
if (TV_GetParent(parent) != 0) {
|
192 |
+
parent := TV_GetParent(parent)
|
193 |
+
}
|
194 |
+
InputBox, new_name, , Please enter a name for the new child
|
195 |
+
if (ErrorLevel == 0){
|
196 |
+
TV_Add(new_name, parent, "Expand")
|
197 |
+
}
|
198 |
+
return
|
199 |
+
UtilDel:
|
200 |
+
selected := TV_GetSelection()
|
201 |
+
if (selected == 0) {
|
202 |
+
MsgBox, Please select an item
|
203 |
+
return
|
204 |
+
}
|
205 |
+
TV_Delete(selected)
|
206 |
+
return
|
207 |
+
|
208 |
+
MySubmit:
|
209 |
+
Gui, Submit, NoHide
|
210 |
+
|
211 |
+
temp_file := FileOpen(file_path . ".temp", "w")
|
212 |
+
parent_id = 0
|
213 |
+
Loop {
|
214 |
+
parent_id := TV_GetNext(parent_id)
|
215 |
+
if (parent_id == 0) {
|
216 |
+
break
|
217 |
+
}
|
218 |
+
TV_GetText(name, parent_id)
|
219 |
+
child_id := TV_GetChild(parent_id)
|
220 |
+
childs := Object()
|
221 |
+
if (child_id != 0) {
|
222 |
+
Loop {
|
223 |
+
TV_GetText(string, child_id)
|
224 |
+
childs[A_Index] := string
|
225 |
+
child_id := TV_GetNext(child_id)
|
226 |
+
if (child_id == 0) {
|
227 |
+
break
|
228 |
+
}
|
229 |
+
}
|
230 |
+
}
|
231 |
+
max := childs.MaxIndex()
|
232 |
+
if (max == "") {
|
233 |
+
temp_file.WriteChar(1)
|
234 |
+
}
|
235 |
+
else {
|
236 |
+
temp_file.WriteChar(max + 1)
|
237 |
+
}
|
238 |
+
WriteString(temp_file, name)
|
239 |
+
Loop, %max% {
|
240 |
+
string := childs[A_Index]
|
241 |
+
WriteString(temp_file, string)
|
242 |
+
}
|
243 |
+
}
|
244 |
+
temp_file.WriteChar(0)
|
245 |
+
temp_file.close()
|
246 |
+
temp_file := FileOpen(file_path . ".temp", "r")
|
247 |
+
|
248 |
+
file := FileOpen(file_path, "w")
|
249 |
+
|
250 |
+
file.WriteChar(10)
|
251 |
+
file.Write("CM3D2_MENU")
|
252 |
+
file.WriteInt(Gversion)
|
253 |
+
|
254 |
+
WriteString(file, Gpath)
|
255 |
+
WriteString(file, Gname)
|
256 |
+
WriteString(file, Gcategory)
|
257 |
+
WriteString(file, Gsetumei)
|
258 |
+
|
259 |
+
file.WriteInt(temp_file.Length)
|
260 |
+
temp_file.RawRead(data, temp_file.Length)
|
261 |
+
file.RawWrite(data, temp_file.Length)
|
262 |
+
|
263 |
+
file.close()
|
264 |
+
temp_file.close()
|
265 |
+
FileDelete, %file_path%.temp
|
266 |
+
|
267 |
+
SplitPath, file_path, OutFileName, OutDir, OutExtension, OutNameNoExt, OutDrive
|
268 |
+
MsgBox, %OutFileName% has been saved
|
269 |
+
return
|
270 |
+
|
271 |
+
GuiEscape:
|
272 |
+
GuiClose:
|
273 |
+
ExitApp
|
274 |
+
return
|