10cd2275ba20f730996c01e9e3ad67f6f0b9b657
[blender-addons-contrib.git] / oscurart_mesh_cache_tools.py
1 bl_info = {
2     "name": "Mesh Cache Tools",
3     "author": "Oscurart",
4     "version": (1, 0, 1),
5     "blender": (2, 70, 0),
6     "location": "Tools > Mesh Cache Tools",
7     "description": "Tools for Management Mesh Cache Process",
8     "warning": "",
9     "wiki_url": "",
10     "tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/",
11     "category": "Import-Export"}
12
13
14 import bpy
15 import os
16 import struct
17 from bpy_extras.io_utils import ImportHelper
18 from bpy.types import (
19         Operator,
20         Panel,
21         PropertyGroup,
22         AddonPreferences,
23         )
24 from bpy.props import (
25         BoolProperty,
26         IntProperty,
27         StringProperty,
28         PointerProperty,
29         CollectionProperty,
30         )
31 from bpy.app.handlers import persistent
32
33
34 class OscurartMeshCacheModifiersSettings(PropertyGroup):
35     array: BoolProperty(default=True)
36     bevel: BoolProperty(default=True)
37     boolean: BoolProperty(default=True)
38     build: BoolProperty(default=True)
39     decimate: BoolProperty(default=True)
40     edge_split: BoolProperty(default=True)
41     mask: BoolProperty(default=True)
42     mirror: BoolProperty(default=True)
43     multires: BoolProperty(default=True)
44     remesh: BoolProperty(default=True)
45     screw: BoolProperty(default=True)
46     skin: BoolProperty(default=True)
47     solidify: BoolProperty(default=True)
48     subsurf: BoolProperty(default=True)
49     triangulate: BoolProperty(default=True)
50     wireframe: BoolProperty(default=True)
51     cloth: BoolProperty(default=True)
52
53
54 # ----------------- AUTO LOAD PROXY
55
56 # bpy.context.scene.pc_auto_load_proxy.remove(0)
57 class CreaPropiedades(Operator):
58     bl_idname = "scene.pc_auto_load_proxy_create"
59     bl_label = "Create Auto Load PC Proxy List"
60
61     def execute(self, context):
62         for gr in bpy.data.collections:
63             if gr.library is not None:
64                 i = bpy.context.scene.pc_auto_load_proxy.add()
65                 i.name = gr.name
66                 i.use_auto_load = False
67         return {'FINISHED'}
68
69
70 class RemuevePropiedades(Operator):
71     bl_idname = "scene.pc_auto_load_proxy_remove"
72     bl_label = "Remove Auto Load PC Proxy List"
73
74     def execute(self, context):
75         for i in bpy.context.scene.pc_auto_load_proxy:
76             bpy.context.scene.pc_auto_load_proxy.remove(0)
77         return {'FINISHED'}
78
79
80 class OscurartMeshCacheSceneAutoLoad(PropertyGroup):
81     name: StringProperty(
82             name="GroupName",
83             default=""
84             )
85     use_auto_load: BoolProperty(
86             name="Bool",
87             default=False
88             )
89
90
91 @persistent
92 def CargaAutoLoadPC(dummy):
93     for gr in bpy.context.scene.pc_auto_load_proxy:
94         if gr.use_auto_load:
95             for ob in bpy.data.collections[gr.name].objects:
96                 for MOD in ob.modifiers:
97                     if MOD.type == "MESH_CACHE":
98                         MOD.cache_format = "PC2"
99                         MOD.forward_axis = "POS_Y"
100                         MOD.up_axis = "POS_Z"
101                         MOD.flip_axis = set(())
102                         MOD.frame_start = bpy.context.scene.pc_pc2_start
103                         abspath = os.path.abspath(bpy.path.abspath("//" + bpy.context.scene.pc_pc2_folder))
104                         MOD.filepath = "%s/%s.pc2" % (abspath, ob.name)
105
106
107 bpy.app.handlers.load_post.append(CargaAutoLoadPC)
108
109
110 # - PANELS -
111
112 class View3DMCPanel():
113     bl_space_type = 'VIEW_3D'
114     bl_region_type = 'TOOLS'
115
116
117 class OscEPc2ExporterPanel(View3DMCPanel, Panel):
118     bl_category = "Tools"
119     bl_label = "Mesh Cache Tools"
120     bl_options = {'DEFAULT_CLOSED'}
121
122     def draw(self, context):
123         layout = self.layout
124         scene = context.scene
125
126         row = layout.column(align=1)
127         row.prop(scene, "pc_pc2_folder", text="Folder")
128         row.operator("buttons.set_meshcache_folder", icon='FILEBROWSER', text="Select Folder Path")
129         row = layout.box().column(align=1)
130         row.label(text="EXPORTER:")
131         row.operator("group.linked_group_to_local", text="Linked To Local", icon="LINKED")
132         row.operator("object.remove_subsurf_modifier", text="Remove Gen Modifiers", icon="MOD_SUBSURF")
133         row.prop(scene.mesh_cache_tools_settings, "array", text="Array")
134         row.prop(scene.mesh_cache_tools_settings, "bevel", text="Bevel")
135         row.prop(scene.mesh_cache_tools_settings, "boolean", text="Boolean")
136         row.prop(scene.mesh_cache_tools_settings, "build", text="Build")
137         row.prop(scene.mesh_cache_tools_settings, "decimate", text="Decimate")
138         row.prop(scene.mesh_cache_tools_settings, "edge_split", text="Edge Split")
139         row.prop(scene.mesh_cache_tools_settings, "mask", text="Mask")
140         row.prop(scene.mesh_cache_tools_settings, "mirror", text="Mirror")
141         row.prop(scene.mesh_cache_tools_settings, "multires", text="Multires")
142         row.prop(scene.mesh_cache_tools_settings, "remesh", text="Remesh")
143         row.prop(scene.mesh_cache_tools_settings, "screw", text="Screw")
144         row.prop(scene.mesh_cache_tools_settings, "skin", text="Skin")
145         row.prop(scene.mesh_cache_tools_settings, "solidify", text="Solidify")
146         row.prop(scene.mesh_cache_tools_settings, "subsurf", text="Subsurf")
147         row.prop(scene.mesh_cache_tools_settings, "triangulate", text="Triangulate")
148         row.prop(scene.mesh_cache_tools_settings, "wireframe", text="Wireframe")
149
150         # row = layout.column(align=1)
151         row.prop(scene, "pc_pc2_start", text="Frame Start")
152         row.prop(scene, "pc_pc2_end", text="Frame End")
153         row.prop(scene, "pc_pc2_exclude", text="Exclude Token:")
154         row.prop_search(scene, "pc_pc2_group", bpy.data, "collections", text="")
155         row.operator("export_shape.pc2_selection", text="Export!", icon="POSE_DATA")
156         row.prop(scene, "pc_pc2_world_space", text="World Space")
157         row = layout.box().column(align=1)
158         row.label(text="IMPORTER:")
159         row.operator("import_shape.pc2_selection", text="Import", icon="POSE_DATA")
160         row.operator("object.modifier_mesh_cache_up", text="MC Top", icon="TRIA_UP")
161         row = layout.box().column(align=1)
162         row.label(text="PROXY AUTO LOAD:")
163         row.operator("scene.pc_auto_load_proxy_create", text="Create List", icon="GROUP")
164         row.operator("scene.pc_auto_load_proxy_remove", text="Remove List", icon="X")
165
166         for i in scene.pc_auto_load_proxy:
167             if bpy.data.collections[i.name].library is not None:
168                 row = layout.row()
169                 row.prop(bpy.data.collections[i.name], "name", text="")
170                 row.prop(i, "use_auto_load", text="")
171
172
173 def OscSetFolder(self, context, filepath):
174     fp = filepath if os.path.isdir(filepath) else os.path.dirname(filepath)
175     try:
176         os.chdir(os.path.dirname(bpy.data.filepath))
177     except Exception as e:
178         self.report({'WARNING'}, "Folder could not be set: {}".format(e))
179         return {'CANCELLED'}
180
181     rfp = os.path.relpath(fp)
182     for sc in bpy.data.scenes:
183         sc.pc_pc2_folder = rfp
184     return {'FINISHED'}
185
186
187 class OscMeshCacheButtonSet(Operator, ImportHelper):
188     bl_idname = "buttons.set_meshcache_folder"
189     bl_label = "Set Mesh Cache Folder"
190     filename_ext = ".txt"
191
192     def execute(self, context):
193         return OscSetFolder(self, context, self.filepath)
194
195
196 def OscFuncExportPc2(self):
197     start = bpy.context.scene.pc_pc2_start
198     end = bpy.context.scene.pc_pc2_end
199     folderpath = bpy.context.scene.pc_pc2_folder
200     framerange = end - start
201
202     for ob in bpy.data.collections[bpy.context.scene.pc_pc2_group].objects[:]:
203         if any(token not in ob.name for token in bpy.context.scene.pc_pc2_exclude.split(",")):
204             bpy.context.window_manager.progress_begin(0, 100)  # progressbar
205             if ob.type == "MESH":
206                 with open("%s/%s.pc2" % (os.path.normpath(folderpath), ob.name), mode="wb") as file:
207                     # header
208                     headerFormat = '<12siiffi'
209                     headerStr = struct.pack(headerFormat,
210                              b'POINTCACHE2\0', 1, len(ob.data.vertices[:]), 0, 1.0, (end + 1) - start)
211                     file.write(headerStr)
212                     # bake
213                     obmat = ob.matrix_world
214                     for i, frame in enumerate(range(start, end + 1)):
215                         print("Percentage of %s bake: %s " % (ob.name, i * 100 / framerange))
216                         bpy.context.window_manager.progress_update(i * 100 / framerange)  # progressbarUpdate
217                         bpy.context.scene.frame_set(frame)
218                         me = bpy.data.meshes.new_from_object(
219                                     scene=bpy.context.scene,
220                                     object=ob,
221                                     apply_modifiers=True,
222                                     settings="RENDER",
223                                     calc_tessface=True,
224                                     calc_undeformed=False
225                                     )
226                         # rotate
227                         if bpy.context.scene.pc_pc2_world_space:
228                             me.transform(obmat)
229                             me.calc_normals()
230                         # create archive
231                         for vert in me.vertices[:]:
232                             file.write(struct.pack("<3f", *vert.co))
233                         # drain mesh
234                         bpy.data.meshes.remove(me)
235
236                     print("%s Bake finished!" % (ob.name))
237
238             bpy.context.window_manager.progress_end()  # progressBarClose
239     print("Bake Totally Finished!")
240
241
242 class OscPc2ExporterBatch(Operator):
243     bl_idname = "export_shape.pc2_selection"
244     bl_label = "Export pc2 for selected Objects"
245     bl_description = "Export pc2 for selected Objects"
246     bl_options = {'REGISTER', 'UNDO'}
247
248     @classmethod
249     def poll(cls, context):
250         return(bpy.context.scene.pc_pc2_group != "" and bpy.context.scene.pc_pc2_folder != 'Set me Please!')
251
252     def execute(self, context):
253         OscFuncExportPc2(self)
254         return {'FINISHED'}
255
256
257 class OscRemoveSubsurf(Operator):
258     bl_idname = "object.remove_subsurf_modifier"
259     bl_label = "Remove Subdivision Surface Modifier"
260     bl_description = "Remove Subdivision Surface Modifier"
261     bl_options = {'REGISTER', 'UNDO'}
262
263     @classmethod
264     def poll(cls, context):
265         return(bpy.context.scene.pc_pc2_group != "")
266
267     def execute(self, context):
268         GENERATE = [
269                 'MULTIRES', 'ARRAY', 'BEVEL', 'BOOLEAN', 'BUILD',
270                 'DECIMATE', 'MASK', 'MIRROR', 'REMESH', 'SCREW',
271                 'SKIN', 'SOLIDIFY', 'SUBSURF', 'TRIANGULATE'
272                 ]
273         for OBJ in bpy.data.collections[bpy.context.scene.pc_pc2_group].objects[:]:
274             for MOD in OBJ.modifiers[:]:
275                 if MOD.type in GENERATE:
276                     if eval("bpy.context.scene.mesh_cache_tools_settings.%s" % (MOD.type.lower())):
277                         OBJ.modifiers.remove(MOD)
278
279         return {'FINISHED'}
280
281
282 class OscPc2iMporterBatch(Operator):
283     bl_idname = "import_shape.pc2_selection"
284     bl_label = "Import pc2 for selected Objects"
285     bl_description = "Import pc2 for selected Objects"
286     bl_options = {'REGISTER', 'UNDO'}
287
288     @classmethod
289     def poll(cls, context):
290         return(bpy.context.scene.pc_pc2_folder != 'Set me Please!')
291
292     def execute(self, context):
293         for OBJ in bpy.context.selected_objects[:]:
294             MOD = OBJ.modifiers.new("MeshCache", 'MESH_CACHE')
295             MOD.filepath = "//%s%s%s.pc2" % (bpy.context.scene.pc_pc2_folder, os.sep, OBJ.name)
296             MOD.cache_format = "PC2"
297             MOD.forward_axis = "POS_Y"
298             MOD.up_axis = "POS_Z"
299             MOD.flip_axis = set(())
300             MOD.frame_start = bpy.context.scene.pc_pc2_start
301
302         return {'FINISHED'}
303
304
305 def OscLinkedGroupToLocal():
306     try:
307         ACTOBJ = bpy.context.active_object
308
309         if not ACTOBJ.id_data.instance_collection:
310             return False
311
312         GROBJS = [ob for ob in ACTOBJ.id_data.instance_collection.objects[:] if ob.type == "MESH"]
313
314         for ob in ACTOBJ.id_data.instance_collection.objects[:]:
315             bpy.context.scene.objects.link(ob)
316         NEWGROUP = bpy.data.collections.new("%s_CLEAN" % (ACTOBJ.name))
317         bpy.context.scene.objects.unlink(ACTOBJ)
318         NEWOBJ = []
319         for ob in GROBJS:
320             NEWGROUP.objects.link(ob)
321             NEWOBJ.append(ob)
322     except:
323         return False
324
325     return True
326
327
328 class OscGroupLinkedToLocal(Operator):
329     bl_idname = "group.linked_group_to_local"
330     bl_label = "Group Linked To Local"
331     bl_description = "Group Linked To Local"
332     bl_options = {'REGISTER', 'UNDO'}
333
334     def execute(self, context):
335         group_check = OscLinkedGroupToLocal()
336         if not group_check:
337             self.report({'WARNING'},
338                         "There is no objects to link or the object already linked. Operation Cancelled")
339             return {'CANCELLED'}
340
341         return {'FINISHED'}
342
343
344 class OscMeshCacheUp(Operator):
345     bl_idname = "object.modifier_mesh_cache_up"
346     bl_label = "Mesh Cache To Top"
347     bl_description = "Send Mesh Cache Modifiers top"
348     bl_options = {'REGISTER', 'UNDO'}
349
350     @classmethod
351     def poll(cls, context):
352         obj = context.object
353         return (obj and obj.type == "MESH")
354
355     def execute(self, context):
356
357         actob = bpy.context.view_layer.objects.active
358
359         for ob in bpy.context.selected_objects[:]:
360             bpy.context.view_layer.objects.active = ob
361             for mod in ob.modifiers[:]:
362                 if mod.type == "MESH_CACHE":
363                     for up in range(ob.modifiers.keys().index(mod.name)):
364                         bpy.ops.object.modifier_move_up(modifier=mod.name)
365
366         bpy.context.view_layer.objects.active = actob
367
368         return {'FINISHED'}
369
370
371 # Add-ons Preferences Update Panel
372
373 # Define Panel classes for updating
374 panels = (
375         OscEPc2ExporterPanel,
376         )
377
378
379 def update_panel(self, context):
380     message = "Mesh Cache Tools: Updating Panel locations has failed"
381     try:
382         for panel in panels:
383             if "bl_rna" in panel.__dict__:
384                 bpy.utils.unregister_class(panel)
385
386         for panel in panels:
387             panel.bl_category = context.preferences.addons[__name__].preferences.category
388             bpy.utils.register_class(panel)
389
390     except Exception as e:
391         print("\n[{}]\n{}\n\nError:\n{}".format(__name__, message, e))
392         pass
393
394
395 class OscurartMeshCacheToolsAddonPreferences(AddonPreferences):
396     # this must match the addon name, use '__package__'
397     # when defining this in a submodule of a python package.
398     bl_idname = __name__
399
400     category: StringProperty(
401             name="Category",
402             description="Choose a name for the category of the panel",
403             default="Tools",
404             update=update_panel,
405             )
406
407     def draw(self, context):
408         layout = self.layout
409         row = layout.row()
410         col = row.column()
411         col.label(text="Category:")
412         col.prop(self, "category", text="")
413
414
415 classes = (
416         OscurartMeshCacheModifiersSettings,
417         OscGroupLinkedToLocal,
418         OscMeshCacheButtonSet,
419         OscMeshCacheUp,
420         OscPc2ExporterBatch,
421         OscPc2iMporterBatch,
422         OscRemoveSubsurf,
423         OscurartMeshCacheToolsAddonPreferences,
424         RemuevePropiedades,
425         OscurartMeshCacheSceneAutoLoad,
426         CreaPropiedades,
427         OscEPc2ExporterPanel,
428         )
429
430
431 # Register
432
433 def register():
434     for cls in classes:
435         bpy.utils.register_class(cls)
436
437     from bpy.types import Scene
438     Scene.mesh_cache_tools_settings = PointerProperty(
439                                             type=OscurartMeshCacheModifiersSettings
440                                             )
441     Scene.pc_auto_load_proxy = CollectionProperty(
442                                             type=OscurartMeshCacheSceneAutoLoad
443                                             )
444
445     Scene.pc_pc2_rotx = BoolProperty(default=True, name="Rotx = 90")
446     Scene.pc_pc2_world_space = BoolProperty(default=True, name="World Space")
447     Scene.pc_pc2_modifiers = BoolProperty(default=True, name="Apply Modifiers")
448     Scene.pc_pc2_subsurf = BoolProperty(default=True, name="Turn Off SubSurf")
449     Scene.pc_pc2_start = IntProperty(default=0, name="Frame Start")
450     Scene.pc_pc2_end = IntProperty(default=100, name="Frame End")
451     Scene.pc_pc2_group = StringProperty()
452     Scene.pc_pc2_folder = StringProperty(default="Set me Please!")
453     Scene.pc_pc2_exclude = StringProperty(default="*")
454
455     update_panel(None, bpy.context)
456
457
458 def unregister():
459     for cls in classes:
460         bpy.utils.unregister_class(cls)
461
462     from bpy.types import Scene
463     del Scene.mesh_cache_tools_settings
464     del Scene.pc_auto_load_proxy
465     del Scene.pc_pc2_rotx
466     del Scene.pc_pc2_world_space
467     del Scene.pc_pc2_modifiers
468     del Scene.pc_pc2_subsurf
469     del Scene.pc_pc2_start
470     del Scene.pc_pc2_end
471     del Scene.pc_pc2_group
472     del Scene.pc_pc2_folder
473     del Scene.pc_pc2_exclude
474
475
476 if __name__ == "__main__":
477     register()