Guest User

Untitled

a guest
Nov 20th, 2017
86
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.62 KB | None | 0 0
  1. ### This addon allows you to clean your scene ###
  2. ### remove doubles, recalculate normals, rename objects...###
  3.  
  4. bl_info = {
  5. "name": "Killer Cleaner",
  6. "author": "dono",
  7. "version": (1, 0),
  8. "blender": (2, 79, 0),
  9. "description": "Clean objects of your scene",
  10. "location": "View3D > Tool Shelf > Killer Cleaner",
  11. "warning": "",
  12. "wiki_url": "",
  13. "category": "User",
  14. }
  15.  
  16. ## KILLER CLEANER
  17.  
  18. import bpy
  19. from bpy import context
  20. import bmesh
  21. import math
  22. from math import pi
  23. from bpy.props import *
  24. from mathutils import Matrix
  25.  
  26. ## DECLARE
  27.  
  28. myModifierList = []
  29. myList = []
  30.  
  31.  
  32. ## MENU text and icon
  33. my_bool = {'remove_doubles':["LATTICE_DATA","Remove duplicate doubles"],
  34. 'tris_to_quad':["OUTLINER_OB_LATTICE","Join triangle into quad"],
  35. 'recalculate_normals':["FACESEL","Recalculate outside"],
  36. 'clear_custom_normal':["UV_FACESEL","Remove the custom split normals layer, if it exists"],
  37. #'apply_modifiers':"MODIFIER",
  38. 'double_sided':["MOD_BEVEL","Display the mesh with double sided lighting (OpenGL only)"],
  39. 'apply_scale':["NDOF_TRANS","Apply the object's transformation to its data"],
  40. 'autosmooth':["SURFACE_NCIRCLE","Auto smooth"],
  41. 'remove_material':["MATERIAL_DATA","Remove material"],
  42. 'rename_objects':["FONT_DATA", "Rename objects with 'GEO' + the Scene name"],
  43. 'make_single_user':["OUTLINER_OB_GROUP_INSTANCE","Make link data local"]}
  44.  
  45. ## CLASS KillerCleanerSettings
  46. class KillerCleanerSettings(bpy.types.PropertyGroup):
  47. polycount_before = bpy.props.IntProperty()
  48. polycount_after = bpy.props.IntProperty()
  49. lenModifierList = bpy.props.IntProperty()
  50.  
  51.  
  52. for i in my_bool:
  53. setattr(KillerCleanerSettings,i,bpy.props.BoolProperty(name=i.replace('_',' ').title(), description=my_bool[i][1].replace('_',' '), default =True))
  54.  
  55. ## CLASS to show menu
  56. class DialogOperator(bpy.types.Operator):
  57.  
  58. bl_idname = "object.dialog_operator"
  59. bl_label = "Clean selected objects"
  60.  
  61. ## EXECUTE
  62. def execute(self, context):
  63.  
  64. ## DECLARE
  65. scene = context.scene
  66. decor = scene.name
  67. override = bpy.context.copy()
  68. myList = []
  69. myModifierList = []
  70. settings = scene.killer_cleaner_settings
  71. settings.lenModifierList = 0
  72.  
  73. ## PROGRESS BAR
  74. wm = bpy.context.window_manager
  75. tot = len(bpy.context.selected_objects)
  76. wm.progress_begin(0, tot)
  77.  
  78. ## START
  79.  
  80. wm = context.window_manager
  81. settings = context.scene.killer_cleaner_settings
  82. settings.polycount_before = 0
  83. settings.polycount_after = 0
  84.  
  85. for index,ob in enumerate(bpy.context.selected_objects) :
  86. if ob.type == 'MESH':
  87. settings.polycount_before+=len(ob.data.polygons)
  88.  
  89. ## DELETE ALL NAMES
  90. if settings.rename_objects == True:
  91. for ob in bpy.context.selected_objects:
  92. ob.name=""
  93.  
  94. ## FOR IN SELECTED OBJECTS
  95. for index,ob in enumerate(bpy.context.selected_objects) :
  96.  
  97. ## PROGRESS BAR
  98. wm.progress_update(index/100)
  99.  
  100. ## IF GROUP
  101. if ob.type == 'EMPTY':
  102. continue
  103.  
  104. ## RENAME OBJECT AND MESH
  105. if settings.rename_objects == True:
  106. ind = str(index).zfill(3)
  107. ob.name = "GEO_"+decor+"_"+ str(ind)
  108. ob.data.name = "GEO_DATA_"+decor+"_"+ str(ind)
  109.  
  110. ## IF MESH
  111. if ob.type == 'MESH':
  112. override = context.copy()
  113. override['object'] = ob
  114. override['active_object'] = ob
  115. new_mesh = ob.data
  116.  
  117. # ## APPLY MODIFIERS
  118. # old_mesh = ob.data
  119. # new_mesh = ob.to_mesh (scene, True, 'PREVIEW')
  120. # ob.modifiers.clear()
  121. # ob.data = new_mesh
  122.  
  123. ## ENTER BMESH
  124. bm = bmesh.new()
  125. bm.from_mesh(new_mesh)
  126.  
  127. ## REMOVE DOUBLES
  128. if settings.remove_doubles == True:
  129. bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.0)
  130.  
  131. ## TRIS TO QUADS
  132. if settings.tris_to_quad == True:
  133. bmesh.ops.join_triangles(bm, faces = bm.faces,angle_face_threshold = pi,angle_shape_threshold =pi)
  134.  
  135. ## RECALCULATE NORMALS
  136. if settings.recalculate_normals == True:
  137. bmesh.ops.recalc_face_normals(bm, faces=bm.faces)
  138.  
  139. ## QUIT BMESH
  140. bm.to_mesh(new_mesh)
  141. new_mesh.update()
  142. bm.clear()
  143.  
  144. ## CLEAR CUSTOM SPLIT NORMALS
  145. if settings.clear_custom_normal == True:
  146. bpy.ops.mesh.customdata_custom_splitnormals_clear(override)
  147.  
  148. ## MAKE SINGLE USER OBJECT DATA
  149. #if ob.data.users>1:
  150.  
  151. if settings.make_single_user == True:
  152. ob.data = ob.data.copy()
  153.  
  154. ## APPLY SCALE (if no modifier)
  155. if settings.apply_scale == True:
  156. if ob.modifiers:
  157. myModifierList.append(ob.name)
  158. settings.lenModifierList +=1
  159. else:
  160. mat = Matrix()
  161. mat[0][0], mat[1][1], mat[2][2] = ob.matrix_world.to_scale()
  162. ob.data.transform(mat)
  163. ob.matrix_world = ob.matrix_world.normalized()
  164.  
  165. ## AUTO SMOOTH
  166. if settings.autosmooth == True:
  167. ob.data.use_auto_smooth = True
  168. ob.data.auto_smooth_angle = math.radians(30)
  169. for poly in ob.data.polygons:
  170. poly.use_smooth = True
  171.  
  172. ## DOUBLE SIDED
  173. if settings.double_sided == True:
  174. ob.data.show_double_sided = True
  175.  
  176. ## REMOVE MATERIALS
  177. if settings.remove_material == True:
  178. ob.data.materials.clear(True)
  179.  
  180. ## PRINT NAME AND POLYCOUNT len(object.data.vertices)
  181. #print (index, "/", len(bpy.context.selected_objects))
  182. myList.append((ob.name, len(ob.data.polygons)))
  183.  
  184. ## PRINT LIST
  185. #for item in sorted(myList, key=lambda a : a[1], reverse=False):
  186. # print (item)
  187. for index,ob in enumerate(bpy.context.selected_objects) :
  188. if ob.type == 'MESH':
  189. settings.polycount_after+=len(ob.data.polygons)
  190.  
  191. # bpy.ops.object.select_all(action='TOGGLE')
  192.  
  193. ## SELECT OBJECTS WITH MODIFIER
  194. for ob in bpy.data.objects:
  195. ob.select = False
  196.  
  197. for modifiers in myModifierList:
  198. #print(settings.lenModifierList)
  199. bpy.data.objects[modifiers].select=True
  200.  
  201. ## END PROGRESS BAR
  202. wm.progress_end()
  203. self.report({'INFO'}, "FINISHED!")
  204. bpy.ops.object.dialog_operator2('INVOKE_DEFAULT')
  205. return {'FINISHED'}
  206.  
  207.  
  208. # def invoke(self, context, event):
  209. # wm = context.window_manager
  210. # settings = context.scene.killer_cleaner_settings
  211. # settings.polycount_before = 0
  212. # settings.polycount_after = 0
  213. #
  214. # for index,ob in enumerate(bpy.context.selected_objects) :
  215. # if ob.type == 'MESH':
  216. # settings.polycount_before+=len(ob.data.polygons)
  217. # return wm.invoke_props_dialog(self)
  218.  
  219.  
  220. ## CLASS to show results
  221. class DialogOperator2(bpy.types.Operator):
  222.  
  223. bl_idname = "object.dialog_operator2"
  224. bl_label = "FINISHED !"
  225.  
  226. def draw(self,context):
  227. settings = context.scene.killer_cleaner_settings
  228. polycount_before = settings.polycount_before
  229. polycount_after = settings.polycount_after
  230.  
  231. layout = self.layout
  232. row = layout.row()
  233. layout.label(text="Killer Cleaner has removed %s faces"%(polycount_before-polycount_after), icon='SOLO_ON')
  234. row = layout.row()
  235. row = layout.row()
  236. row = layout.row()
  237. row = layout.row()
  238. row = layout.row()
  239. row = layout.row()
  240. row = layout.row()
  241. row = layout.row()
  242. row = layout.row()
  243. if settings.apply_scale == True:
  244. if settings.lenModifierList>0:
  245. layout.label(text="Scale not applied on %01d" % settings.lenModifierList +" objects" , icon="OUTLINER_OB_LAMP")
  246. layout.label(text="These objects with modifiers have been selected", icon='VISIBLE_IPO_ON')
  247.  
  248. def execute(self, context):
  249. self.report({'INFO'}, "OBJECTS WITH MODIFIERS SELECTED !")
  250. return {'FINISHED'}
  251.  
  252. def invoke(self, context, event):
  253. wm = context.window_manager
  254. return wm.invoke_props_dialog(self) #(self, width=400, height=400)
  255.  
  256. ### Panel Killer Cleaner launch ###
  257. class CleanerPanel(bpy.types.Panel):
  258.  
  259. bl_idname = "override"
  260. bl_label = "Killer Cleaner"
  261. bl_space_type = "VIEW_3D"
  262. bl_region_type = "TOOLS"
  263. bl_category = "Killer CLeaner"
  264.  
  265. def draw(self, context):
  266. bl_idname = "object.dialog_operator"
  267. bl_label = "KILLER CLEANER"
  268.  
  269. def draw(self,context):
  270.  
  271. ## PANEL
  272. layout = self.layout
  273. settings = context.scene.killer_cleaner_settings
  274.  
  275.  
  276. row=layout.row()
  277. row=layout.row()
  278. row=layout.row()
  279.  
  280. for prop,icon in my_bool.items():
  281. layout.prop(settings, prop, icon=icon[0])
  282.  
  283. #row.label(icon=icon)
  284. #row.prop(settings, prop)
  285.  
  286. row=layout.row()
  287. row=layout.row()
  288.  
  289. layout = self.layout
  290. layout = self.layout
  291. layout.operator("object.dialog_operator", icon = "SOLO_ON") #Create button Assign
  292.  
  293.  
  294.  
  295. ### Register / Unregister ###
  296. def register():
  297.  
  298. ## REGISTER
  299. bpy.utils.register_class(KillerCleanerSettings)
  300. bpy.utils.register_class(DialogOperator)
  301. bpy.utils.register_class(DialogOperator2)
  302. bpy.types.Scene.killer_cleaner_settings = bpy.props.PointerProperty(type = KillerCleanerSettings)
  303. bpy.utils.register_class(CleanerPanel)
  304.  
  305. def unregister():
  306. bpy.utils.unregister_class(CleanerPanel)
  307.  
  308. if __name__ == "__main__":
  309. register()
  310.  
  311.  
  312. # call test
  313. #bpy.ops.object.dialog_operator('INVOKE_DEFAULT')
Add Comment
Please, Sign In to add comment