-
Notifications
You must be signed in to change notification settings - Fork 29
/
__init__.py
361 lines (307 loc) · 14.4 KB
/
__init__.py
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
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
# blender plugin version is auto-synced with pyproject.toml version
bl_info = {
"name": "Frontier's Cobra Engine Formats",
"author": "Harlequinz Ego, HENDRIX et al.",
"blender": (4, 0, 0),
"version": (2024, 12, 11),
"location": "File > Import-Export",
"description": "Import-Export models, skeletons and animations",
"warning": "",
"wiki_url": "https://github.com/OpenNaja/cobra-tools",
"support": 'COMMUNITY',
"tracker_url": "https://github.com/OpenNaja/cobra-tools/issues/new",
"category": "Import-Export"}
try:
import os
import sys
import subprocess
import logging
import pkg_resources, importlib.util
import bpy
import bpy.utils.previews
from bpy.props import IntProperty
from bpy.types import PropertyGroup
import addon_utils
copies_of_tools = []
for addon in addon_utils.modules():
if addon.bl_info['name'] == bl_info['name']:
copies_of_tools.append(addon)
if len(copies_of_tools) > 1:
addon_paths = "\n".join(os.path.dirname(addon.__file__) for addon in copies_of_tools)
raise UserWarning(f"You have multiple copies of the tools installed in your blender addons folders:\n"
f"{addon_paths}\nClose blender, delete all but the current version and try again.")
plugin_dir = os.path.dirname(__file__)
if not plugin_dir in sys.path:
sys.path.append(plugin_dir)
from ovl_util.logs import logging_setup
logging_setup("blender_plugin")
logging.info(f"Running blender {'.'.join([str(x) for x in bpy.app.version])}")
from plugin import addon_updater_ops
from plugin.addon_updater_ops import classes as updater_classes
from plugin.modules_import.operators import ImportBanis, ImportManis, ImportMatcol, ImportFgm, ImportMS2, ImportSPL, \
ImportVoxelskirt, ImportMS2FromBrowser, ImportFGMFromBrowser
from plugin.modules_export.operators import ExportMS2, ExportSPL, ExportManis, ExportBanis, ExportFgm
from plugin.utils.operators import UpdateFins, UpdateLods, VcolToComb, CombToVcol, TransferHairCombing, AddHair, \
GenerateRigEdit, ApplyPoseAll, ConvertScaleToLoc, ExtrudeFins, IntrudeFins, Mdl2Rename, Mdl2Duplicate, \
AutosmoothAll, EditFlag, SetupRig
from plugin.utils.properties import CobraSceneSettings, CobraMeshSettings, CobraCollisionSettings, \
CobraMaterialSettings, LodData, MATCOL_ListItem
from plugin.utils.panels import COBRA_PT_material, COBRA_PT_model, COBRA_PT_viewport, matcol_slot_updated, \
COBRA_UL_matcol_slot, COBRA_PT_matcols, COBRA_PT_mesh, COBRA_PT_scene, COBRA_PT_collision, COBRA_UL_lod
# mod data
from plugin.mods.properties import ModData, SceneryData
from plugin.mods.panels import COBRA_MOD_PT_mod, COBRA_MOD_PT_scenery
# drag and drop:
from bpy.app.handlers import persistent
from plugin import import_fgm, import_ms2, import_spl
# 4.1 drag and drop
if hasattr(bpy.types, 'FileHandler'):
from plugin.modules_import.operators import MS2_FH_script_import, FGM_FH_script_import
global preview_collection
class CobraPreferences(bpy.types.AddonPreferences):
"""Cobra preferences"""
bl_idname = __package__
# Addon updater preferences.
auto_check_update: bpy.props.BoolProperty(
name="Auto-check for Update",
description="If enabled, auto-check for updates using an interval",
default=False)
updater_interval_months: bpy.props.IntProperty(
name='Months',
description="Number of months between checking for updates",
default=0,
min=0)
updater_interval_days: bpy.props.IntProperty(
name='Days',
description="Number of days between checking for updates",
default=1,
min=0,
max=31)
updater_interval_hours: bpy.props.IntProperty(
name='Hours',
description="Number of hours between checking for updates",
default=0,
min=0,
max=23)
updater_interval_minutes: bpy.props.IntProperty(
name='Minutes',
description="Number of minutes between checking for updates",
default=0,
min=0,
max=59)
def draw(self, context):
# we are only suggesting to install bitarray for now
if not importlib.util.find_spec("bitarray"):
row = self.layout.row()
row.alert = True
row.operator("wm.install_dependencies", icon="ERROR")
addon_updater_ops.update_settings_ui(self, context)
class InstallDependencies(bpy.types.Operator):
"""Installs: bitarray"""
bl_idname = "wm.install_dependencies"
bl_label = "Click to install missing dependencies, requires restarting"
bl_options = {'REGISTER'}
def execute(self, context):
# from the suggested modules list, remove those installed already
# pkg_resources might not look into the addon-packages folder
missing = {'bitarray'} - {pkg.key for pkg in pkg_resources.working_set}
python = sys.executable
# can't write in site-packages, but we can write in the addon-packages folder
subprocess.call([python, '-m', 'pip', 'install', *missing, '-t', os.path.join(bpy.utils.user_resource("SCRIPTS"), 'addons', 'modules')], stdout=subprocess.DEVNULL)
return {'FINISHED'}
def draw_rigid_body_constraints_cobra(self, context):
layout = self.layout
col = layout.column(align=True)
col.prop(context.active_object.cobra_coll, "plasticity_min")
col.prop(context.active_object.cobra_coll, "plasticity_max")
def menu_func_export(self, context):
icon = preview_collection["frontier.png"].icon_id
self.layout.operator(ExportFgm.bl_idname, text="Cobra Material (.fgm)", icon_value=icon)
self.layout.operator(ExportMS2.bl_idname, text="Cobra Model (.ms2)", icon_value=icon)
self.layout.operator(ExportSPL.bl_idname, text="Cobra Spline (.spl)", icon_value=icon)
self.layout.operator(ExportBanis.bl_idname, text="Cobra Baked Anim (.banis)", icon_value=icon)
self.layout.operator(ExportManis.bl_idname, text="Cobra Anim (.manis)", icon_value=icon)
def menu_func_import(self, context):
icon = preview_collection["frontier.png"].icon_id
self.layout.operator(ImportFgm.bl_idname, text="Cobra Materials (.fgm)", icon_value=icon)
self.layout.operator(ImportMatcol.bl_idname, text="Cobra Layered Material (.matcol, .dinosaurmateriallayers)",
icon_value=icon)
self.layout.operator(ImportMS2.bl_idname, text="Cobra Models (.ms2)", icon_value=icon)
self.layout.operator(ImportBanis.bl_idname, text="Cobra Baked Anim (.banis)", icon_value=icon)
self.layout.operator(ImportManis.bl_idname, text="Cobra Anim (.manis)", icon_value=icon)
self.layout.operator(ImportSPL.bl_idname, text="Cobra Splines (.spl)", icon_value=icon)
self.layout.operator(ImportVoxelskirt.bl_idname, text="Cobra Map (.voxelskirt)", icon_value=icon)
# Function used to inject elements in the contextual menu of the File Browser editor
def CT_FileBrowser_Context_Menu(self, context):
if context.space_data.browse_mode == 'FILES' and context.active_file:
file = context.active_file.name
folder = context.space_data.params.directory.decode('ascii')
filepath = os.path.join(folder, file)
fileext = os.path.splitext(file)[1]
if os.path.isfile(filepath):
if fileext.lower() == ".ms2":
layout = self.layout
layout.separator()
layout.operator(ImportMS2FromBrowser.bl_idname)
if fileext.lower() == ".fgm":
layout = self.layout
layout.separator()
layout.operator(ImportFGMFromBrowser.bl_idname)
# Fake operator-like class to support calling import functions without an actual operator (reporter needed)
class MockUpReporter:
def show_info(self, msg: str):
logging.info(msg)
def show_warning(self, msg: str):
logging.warning(msg)
def show_error(self, exception: Exception):
logging.exception('Got exception on main handler')
def report_messages(self, class_method, *args, **kwargs):
try:
class_method(self, *args, **kwargs)
result = {'FINISHED'}
except Exception as err:
self.show_error(err)
result = {'CANCELLED'}
return result
# Function to handle drag&drop of cobra files into blender
@persistent
def cobra_viewport3d_drop_handler(scene, depsgraph):
obj = bpy.context.active_object
try:
if obj and obj.type == 'EMPTY' and obj.data.type == 'IMAGE':
# when dropping something to the 3d view it will create an image by default but will keep the file path
# as .filepath, we can use that to find if the dropped file is a fgm or ms2, delete the empty object and
# load the actual asset instead.
filepath = obj.data.filepath
if filepath.lower().endswith(".ms2"):
"""We have a ms2 loaded as image"""
bpy.data.images.remove(obj.data)
bpy.data.objects.remove(obj)
rep = MockUpReporter()
rep.report_messages(import_ms2.load, filepath=filepath, use_custom_normals=True)
if filepath.lower().endswith(".fgm"):
"""We have a fgm loaded as image"""
bpy.data.images.remove(obj.data)
bpy.data.objects.remove(obj)
rep = MockUpReporter()
rep.report_messages(import_fgm.load, filepath=filepath, replace=False)
if filepath.lower().endswith(".spl"):
"""We have a spline loaded as image"""
bpy.data.images.remove(obj.data)
bpy.data.objects.remove(obj)
rep = MockUpReporter()
rep.report_messages(import_spl.load, filepath=filepath)
except:
pass
classes = (
*updater_classes,
AddHair,
ApplyPoseAll,
AutosmoothAll,
COBRA_PT_collision,
COBRA_PT_matcols,
COBRA_PT_material,
COBRA_PT_mesh,
COBRA_PT_model,
COBRA_PT_scene,
COBRA_PT_viewport,
COBRA_UL_lod,
COBRA_UL_matcol_slot,
COBRA_MOD_PT_mod,
COBRA_MOD_PT_scenery,
ModData,
SceneryData,
CobraCollisionSettings,
CobraMaterialSettings,
CobraMeshSettings,
CobraPreferences,
CobraSceneSettings,
CombToVcol,
ConvertScaleToLoc,
EditFlag,
ExportBanis,
ExportFgm,
ExportMS2,
ExportManis,
ExportSPL,
ExtrudeFins,
GenerateRigEdit,
ImportBanis,
ImportFGMFromBrowser,
ImportFgm,
ImportMS2,
ImportMS2FromBrowser,
ImportManis,
ImportMatcol,
ImportSPL,
ImportVoxelskirt,
InstallDependencies,
IntrudeFins,
LodData,
MATCOL_ListItem,
Mdl2Duplicate,
Mdl2Rename,
SetupRig,
TransferHairCombing,
UpdateFins,
UpdateLods,
VcolToComb,
)
if hasattr(bpy.types, 'FileHandler'):
classes += (MS2_FH_script_import, FGM_FH_script_import)
except:
logging.exception("Startup failed")
pass
def register():
addon_updater_ops.register(bl_info)
icons_dir = os.path.join(plugin_dir, "icons")
global preview_collection
preview_collection = bpy.utils.previews.new()
for icon_name_ext in os.listdir(icons_dir):
icon_name = os.path.basename(icon_name_ext)
preview_collection.load(icon_name, os.path.join(icons_dir, icon_name_ext), 'IMAGE')
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
# insert properties
bpy.types.Material.fgm = bpy.props.PointerProperty(type=CobraMaterialSettings)
bpy.types.Material.matcol_layers = bpy.props.CollectionProperty(type=MATCOL_ListItem)
bpy.types.Material.matcol_layers_current = bpy.props.IntProperty(
name="Index for matcol layers", default=0, update=matcol_slot_updated)
bpy.types.Scene.cobra = bpy.props.PointerProperty(type=CobraSceneSettings)
bpy.types.Mesh.cobra = bpy.props.PointerProperty(type=CobraMeshSettings)
bpy.types.Object.cobra_coll = bpy.props.PointerProperty(type=CobraCollisionSettings)
# mod properties
bpy.types.Collection.mod = bpy.props.PointerProperty(type=ModData)
bpy.types.Object.scenery = bpy.props.PointerProperty(type=SceneryData)
# Injection of elements in the contextual menu of the File Browser editor
bpy.types.FILEBROWSER_MT_context_menu.append(CT_FileBrowser_Context_Menu)
bpy.types.PHYSICS_PT_rigid_body_constraint_limits_angular.append(draw_rigid_body_constraints_cobra)
# handle drag and drop of custom files:
if not hasattr(bpy.types, 'FileHandler'):
bpy.app.handlers.depsgraph_update_post.append(cobra_viewport3d_drop_handler)
def unregister():
# Injection of elements in the contextual menu of the File Browser editor
bpy.types.FILEBROWSER_MT_context_menu.remove(CT_FileBrowser_Context_Menu)
bpy.types.PHYSICS_PT_rigid_body_constraint_limits_angular.remove(draw_rigid_body_constraints_cobra)
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
for cls in reversed(classes):
try:
# there seems to be an error due to previously removed class that prevents the plugin from
# disabling safely.
bpy.utils.unregister_class(cls)
except:
pass
del bpy.types.Material.matcol_layers
del bpy.types.Material.matcol_layers_current
del bpy.types.Scene.cobra
del bpy.types.Mesh.cobra
global preview_collection
bpy.utils.previews.remove(preview_collection)
if not hasattr(bpy.types, 'FileHandler'):
bpy.app.handlers.depsgraph_update_post.remove(cobra_viewport3d_drop_handler)
if __name__ == "__main__":
print("Main")
register()