Repository navigation
Expand file tree
/
Copy path__init__.py
More file actions
251 lines (222 loc) · 9.93 KB
/
Copy path__init__.py
File metadata and controls
251 lines (222 loc) · 9.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
bl_info = {
"name": "Modding Toolkit",
"author": "Dimcirui",
"version": (2, 8, 5),
"blender": (3, 0, 0),
"location": "View3D > Sidebar > MOD Toolkit",
"description": "Modding Toolkit for Capcom's games",
"category": "Object",
}
import bpy
from bpy.props import BoolProperty, IntProperty, StringProperty, EnumProperty
from bpy.types import AddonPreferences
from . import addon_updater_ops
from .core import migrate
from .core import i18n
from .core import standard_ops
from .core import pose_ops
from .core import bone_ops
from .core import mesh_ops
from .core import editor_props
from .core import editor_ops
from .core import mdf_tex_processor_base
from .core import tex_convert_base
from .core import shader_ops
from .core import chain_convert_ops
from .core import mesh_port_ops
from .core import port_consent
from .core import mhwi_port_ops
from .core import mdf_port_ops
from .core import mrl3_port_ops
from .core import ctc_port_ops
from .core import mhwi_batch_port_ops
from .core import pre_export_check_ops
from .core import pre_export_fix_ops
from .core import ref_model_ops
from .core import stale_cleanup_ops
from . import ui, games
class MT_Preferences(AddonPreferences):
bl_idname = __name__
auto_check_update: BoolProperty(
name="Auto-check for Update",
description="If enabled, auto-check for updates using an interval",
default=False,
)
updater_interval_months: IntProperty(
name='Months', description="Number of months between checking for updates",
default=0, min=0
)
updater_interval_days: IntProperty(
name='Days', description="Number of days between checking for updates",
default=7, min=0,
)
updater_interval_hours: IntProperty(
name='Hours', description="Number of hours between checking for updates",
default=0, min=0, max=23
)
updater_interval_minutes: IntProperty(
name='Minutes', description="Number of minutes between checking for updates",
default=0, min=0, max=59
)
show_console_on_batch_export: BoolProperty(
name="Show Console During Batch Export",
description=(
"Opens the system console before a batch export and leaves it open "
"afterward, so progress and per-file errors can be watched live.\n"
"Windows only. If RE Mesh Editor's or MHW Model Editor's own 'Show "
"Console' option is also enabled, it is temporarily disabled during "
"the batch so it doesn't re-toggle (and hide) the console mid-export.\n"
"Like those addons, this uses Blender's console_toggle(), which can't "
"detect whether the console is already open -- if it's already open "
"when the batch starts, this will close it instead"
),
default=False,
)
# 作者自用工具的侧栏入口。默认关:这些操作符是给特定资产管线写的,对绝大多数
# 使用者没有意义。以前的做法是把入口删掉、只留操作符注册着,结果是自己要用的
# 时候得按 F3 敲 idname —— 这个开关就是那件事的替代品。见
# ui/personal_tools_panel.py。
show_personal_tools: BoolProperty(
name="Show Personal Tools Panel",
description=(
"Adds a 'Personal Tools' panel to the MOD Toolkit sidebar, holding "
"operators written for one specific asset pipeline (Endfield facial "
"vertex-group renaming, and anything else added later).\n"
"Off by default because these are of no use to most people. Addon "
"preferences are keyed by module name, so this has to be ticked "
"separately in each Blender install"
),
default=False,
)
# MHWI:这个 mod 是否要求玩家装"解除部位物理上限"插件。装了以后 arm / wst / leg 与
# body 等同(300–511,各受 255 总数限制)。这是制作者对自己的 mod 做的决定,不是
# 某个 .blend 的属性,而且只会从"不要求"变成"要求",所以放在偏好里永久保存,默认关。
# 移植物理骨骼、拆分并重命名物理骨的弹窗里都会显示并可直接切换。
mhwi_physics_unlocked: BoolProperty(
name="MHWI: Use the \"Remove Physics Limit\" Plugin",
description=(
"Use the \"Remove Physics Limit\" plugin, which lifts the per-part physics bone "
"limit: arm / wst / leg then use IDs 300-511 like body.\n"
"Off: arm / wst / leg simulate only IDs 150-199"
),
default=False,
)
def draw(self, context):
layout = self.layout
layout.prop(self, "show_console_on_batch_export")
layout.prop(self, "show_personal_tools")
layout.prop(self, "mhwi_physics_unlocked",
text=i18n.T("core.standard_ops.graft_unlocked_plugin"))
addon_updater_ops.update_settings_ui(self, context)
# Under the updater UI, because it is the updater's merge-never-delete
# behaviour that creates the leftovers -- see core/stale_cleanup.py.
stale_cleanup_ops.draw_preferences_row(layout)
modules = [
i18n,
editor_props,
editor_ops,
standard_ops,
pose_ops,
# Registered here rather than by ui.main_panel, which is where these
# operators used to live. They own no PropertyGroup and no scene property,
# so their position relative to ui/ does not matter.
bone_ops,
mesh_ops,
mdf_tex_processor_base,
tex_convert_base,
shader_ops,
# Before every cross-game port operator: they read its consent state in
# their poll, and its own operator has to exist for the locked panel to
# have a button to draw.
port_consent,
chain_convert_ops,
mesh_port_ops,
# After mesh_port_ops: it reuses that module's plan execution helpers and its
# collection picker rather than restating either.
mhwi_port_ops,
mdf_port_ops,
# After mdf_port_ops: both reuse its prefab loader and shared "Mod Root" row.
mrl3_port_ops,
# The third MHWI -> MHWilds rebuild, after the other two for readability only;
# it shares no state with them, and drives RE Chain Editor through
# core/re_chain_utils.py rather than reimplementing the builders.
ctc_port_ops,
# Last of the MHWI ports: it drives all three above plus the MHWI importer
# and the MHRS exporter, so it registers after every operator it calls.
mhwi_batch_port_ops,
# After mdf_port_ops: it imports that module's collection picker and shared
# "Mod Root" row rather than restating either.
pre_export_check_ops,
pre_export_fix_ops,
ref_model_ops,
stale_cleanup_ops,
games,
ui,
]
def register():
addon_updater_ops.register(bl_info)
migrate.run()
bpy.utils.register_class(MT_Preferences)
for mod in modules:
mod.register()
_start_chain_patch_timer()
#: Remaining deferred attempts at patching RE Chain's import (see below).
_chain_patch_retries = 20
def _patch_chain_import():
"""给 RE-Chain-Editor 的 chain/chain2 导入打上快速补丁。
上游把 alignChains() 放在链组循环内部,而它扫全场景 + 每节点做一次全量依赖图求值,
导入代价是 O(G²·m)。实测导入 196 组需约 78 分钟(因此容易被误当成卡死而中断,
留下静态看不出异常的残缺数据);补丁后约 32 秒。细节见 core/re_chain_utils.py。
补丁靠扫 sys.modules 找目标,所以**依赖 RE-Chain-Editor 已经加载**。Blender 启用
插件的顺序不保证,冷启动时很可能轮到我们时它还没加载 —— 那一次扫描会一无所获。
因此这里用定时器重试,直到装上或次数用尽;否则补丁只在"先有 RE Chain、再重新启用
本插件"时才生效,而那恰好不是用户的正常启动路径。
RE-Chain-Editor 未安装时会把重试用完然后明确说一声(早先的版本在这条路径上完全
不打日志,等于静默失败)。任何异常都不能影响本插件注册。
"""
global _chain_patch_retries
try:
from .core.re_chain_utils import install_fast_chain_import
n = install_fast_chain_import()
except Exception as e:
print(f"[Modding-Toolkit] fast chain import patch skipped: {e}")
return None
if n:
print(f"[Modding-Toolkit] fast chain import patch applied to {n} binding(s)")
return None
_chain_patch_retries -= 1
if _chain_patch_retries > 0:
return 0.5 # timer: RE Chain Editor may still be loading
print("[Modding-Toolkit] RE Chain Editor not found; its chain import stays "
"unpatched (imports of large chain files will be very slow)")
return None
def _start_chain_patch_timer():
global _chain_patch_retries
# 与上面的导入补丁无关,也不需要等 RE Chain Editor 加载完:订阅的是 Blender 自己
# 的活动物体属性,回调里才去看 re_chain_toolpanel 在不在。
try:
from .core.re_chain_utils import install_chain_type_sync
install_chain_type_sync()
except Exception as e:
print(f"[Modding-Toolkit] chain file type sync skipped: {e}")
_chain_patch_retries = 20
if _patch_chain_import() is not None and not bpy.app.timers.is_registered(
_patch_chain_import):
bpy.app.timers.register(_patch_chain_import, first_interval=0.5,
persistent=True)
def unregister():
addon_updater_ops.unregister()
bpy.utils.unregister_class(MT_Preferences)
if bpy.app.timers.is_registered(_patch_chain_import):
bpy.app.timers.unregister(_patch_chain_import)
try:
from .core.re_chain_utils import (uninstall_fast_chain_import,
uninstall_chain_type_sync)
uninstall_fast_chain_import()
uninstall_chain_type_sync()
except Exception:
pass
for mod in reversed(modules):
mod.unregister()
if __name__ == "__main__":
register()