6
6
Tooltip: 'Clean unused data from all selected mesh objects.'
9
__author__ = ["Campbell Barton"]
10
__url__ = ("blender", "elysiun", "http://members.iinet.net.au/~cpbarton/ideasman/")
15
Cleans unused data from selected meshes
18
# ***** BEGIN GPL LICENSE BLOCK *****
20
# Script copyright (C) Campbell J Barton
22
# This program is free software; you can redistribute it and/or
23
# modify it under the terms of the GNU General Public License
24
# as published by the Free Software Foundation; either version 2
25
# of the License, or (at your option) any later version.
27
# This program is distributed in the hope that it will be useful,
28
# but WITHOUT ANY WARRANTY; without even the implied warranty of
29
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30
# GNU General Public License for more details.
32
# You should have received a copy of the GNU General Public License
33
# along with this program; if not, write to the Free Software Foundation,
34
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
36
# ***** END GPL LICENCE BLOCK *****
37
# --------------------------------------------------------------------------
8
40
from Blender import *
9
41
from Blender.Mathutils import TriangleArea
45
dict2MeshWeight= BPyMesh.dict2MeshWeight
46
meshWeight2Dict= BPyMesh.meshWeight2Dict
11
48
def rem_free_verts(me):
12
vert_users = [0] * len(me.verts)
49
vert_users= [0] * len(me.verts)
15
52
vert_users[v.index]+=1
31
65
def rem_free_edges(me, limit=None):
32
66
''' Only remove based on limit if a limit is set, else remove all '''
34
return min(a,b), max(a,b)
36
edgeDict = {} # will use a set when python 2.4 is standard.
73
edgeDict= {} # will use a set when python 2.4 is standard.
39
for i in xrange(len(f.v)):
40
edgeDict[sortPair(f.v[i].index, f.v[i-1].index)] = None
76
fidxs= [v.index for v in f.v]
77
for i in xrange(len(fidxs)):
78
edgeDict[sortPair(fidxs[i], fidxs[i-1])]= None
44
82
if not edgeDict.has_key(sortPair(e.v1.index, e.v2.index)):
45
83
edges_free.append(e)
48
edges_free = [e for e in edges_free if (e.v1.co-e.v2.co).length <= limit]
86
edges_free= [e for e in edges_free if (e.v1.co-e.v2.co).length <= limit]
50
88
me.edges.delete(edges_free)
51
89
return len(edges_free)
53
91
def rem_area_faces(me, limit=0.001):
54
92
''' Faces that have an area below the limit '''
57
return TriangleArea(f.v[0].co, f.v[1].co, f.v[2].co)
60
TriangleArea(f.v[0].co, f.v[1].co, f.v[2].co) +\
61
TriangleArea(f.v[0].co, f.v[2].co, f.v[3].co)
62
rem_faces = [f for f in me.faces if faceArea(f) <= limit]
93
rem_faces= [f for f in me.faces if f.area <= limit]
64
95
me.faces.delete( 0, rem_faces )
65
96
return len(rem_faces)
67
98
def rem_perimeter_faces(me, limit=0.001):
68
99
''' Faces whos combine edge length is below the limit '''
72
(f.v[0].co-f.v[1].co).length +\
73
(f.v[1].co-f.v[2].co).length +\
74
(f.v[2].co-f.v[0].co).length
77
(f.v[0].co-f.v[1].co).length +\
78
(f.v[1].co-f.v[2].co).length +\
79
(f.v[2].co-f.v[3].co).length +\
80
(f.v[3].co-f.v[0].co).length
81
rem_faces = [f for f in me.faces if faceEdLen(f) <= limit]
104
(v[0].co-v[1].co).length +\
105
(v[1].co-v[2].co).length +\
106
(v[2].co-v[0].co).length
109
(v[0].co-v[1].co).length +\
110
(v[1].co-v[2].co).length +\
111
(v[2].co-v[3].co).length +\
112
(v[3].co-v[0].co).length
113
rem_faces= [f for f in me.faces if faceEdLen(f) <= limit]
83
115
me.faces.delete( 0, rem_faces )
84
116
return len(rem_faces)
89
return Draw.PupFloatInput(text, 0.001, 0.0, 1.0, 0.1, 4)
92
scn = Scene.GetCurrent()
93
obsel = Object.GetSelected()
94
actob = scn.getActiveObject()
96
is_editmode = Window.EditMode()
118
def rem_unused_materials(me):
119
materials= me.materials
120
len_materials= len(materials)
121
if len_materials < 2:
126
material_users= dict( [(i,0) for i in xrange(len_materials)] )
129
# Make sure the face index isnt too big. this happens sometimes.
130
if f.mat >= len_materials:
132
material_users[f.mat] += 1
135
reindex_mapping= dict( [(i,0) for i in xrange(len_materials)] )
140
if material_users[i] == 0:
142
reindex_mapping[i]= mat_idx_subtract
147
f.mat= f.mat - reindex_mapping[f.mat]
149
me.materials= materials
153
def rem_free_groups(me, groupNames, vWeightDict):
154
''' cound how many vert users a group has and remove unsued groups '''
156
groupUserDict= dict([(group,0) for group in groupNames])
158
for vertexWeight in vWeightDict:
159
for group, weight in vertexWeight.iteritems():
160
groupUserDict[group] += 1
166
if groupUserDict[group] == 0:
168
print '\tremoving, vgroup', group
172
def rem_zero_weights(me, limit, groupNames, vWeightDict):
173
''' remove verts from a group when their weight is zero.'''
175
for vertexWeight in vWeightDict:
176
items= vertexWeight.items()
177
for group, weight in items:
179
del vertexWeight[group]
180
rem_vweight_count+= 1
182
return rem_vweight_count
185
def normalize_vweight(me, groupNames, vWeightDict):
186
for vertexWeight in vWeightDict:
188
for group, weight in vertexWeight.iteritems():
191
if unit != 1.0 and unit != 0.0:
192
for group, weight in vertexWeight.iteritems():
193
vertexWeight[group]= weight/unit
198
scn= Scene.GetCurrent()
199
obsel= Object.GetSelected()
200
actob= scn.getActiveObject()
202
is_editmode= Window.EditMode()
98
204
# Edit mode object is not active, add it to the list.
99
205
if is_editmode and (not actob.sel):
100
206
obsel.append(actob)
102
meshes = [ob.getData(mesh=1) for ob in obsel if ob.getType() == 'Mesh']
105
209
#====================================#
106
210
# Popup menu to select the functions #
107
211
#====================================#
110
Draw.PupMenu('ERROR%t|no meshes in selection')
112
method = Draw.PupMenu("""
113
Clean Mesh, Remove...%t|
114
Verts: free standing|
115
Edges: not in a face|
116
Edges: below a length|
117
Faces: below an area|%l|
118
All of the above|""")
123
limit = getLimit('threshold: ')
125
print 'method', method
129
CLEAN_VERTS_FREE = Draw.Create(1)
130
CLEAN_EDGE_NOFACE = Draw.Create(0)
131
CLEAN_EDGE_SMALL = Draw.Create(0)
132
CLEAN_FACE_PERIMETER = Draw.Create(0)
133
CLEAN_FACE_SMALL = Draw.Create(0)
134
limit = Draw.Create(0.01)
213
CLEAN_ALL_DATA= Draw.Create(0)
214
CLEAN_VERTS_FREE= Draw.Create(1)
215
CLEAN_EDGE_NOFACE= Draw.Create(0)
216
CLEAN_EDGE_SMALL= Draw.Create(0)
217
CLEAN_FACE_PERIMETER= Draw.Create(0)
218
CLEAN_FACE_SMALL= Draw.Create(0)
220
CLEAN_MATERIALS= Draw.Create(0)
221
CLEAN_GROUP= Draw.Create(0)
222
CLEAN_VWEIGHT= Draw.Create(0)
223
CLEAN_WEIGHT_NORMALIZE= Draw.Create(0)
224
limit= Draw.Create(0.01)
136
225
# Get USER Options
139
228
('Verts: free', CLEAN_VERTS_FREE, 'Remove verts that are not used by an edge or a face.'),\
140
229
('Edges: free', CLEAN_EDGE_NOFACE, 'Remove edges that are not in a face.'),\
141
230
('Edges: short', CLEAN_EDGE_SMALL, 'Remove edges that are below the length limit.'),\
142
231
('Faces: small perimeter', CLEAN_FACE_PERIMETER, 'Remove faces below the perimeter limit.'),\
143
232
('Faces: small area', CLEAN_FACE_SMALL, 'Remove faces below the area limit (may remove faces stopping T-face artifacts).'),\
234
('Material Clean', CLEAN_MATERIALS, 'Remove unused materials.'),\
236
('Group Clean', CLEAN_GROUP, 'Remove vertex groups that have no verts using them.'),\
237
('Weight Clean', CLEAN_VWEIGHT, 'Remove zero weighted verts from groups (limit is zero threshold).'),\
238
('Weight Normalize', CLEAN_WEIGHT_NORMALIZE, 'Make the sum total of vertex weights accross vgroups 1.0 for each vertex.'),\
144
240
('limit: ', limit, 0.001, 1.0, 'Limit used for the area and length tests above (a higher limit will remove more data).'),\
242
('All Mesh Data', CLEAN_ALL_DATA, 'Warning! Operate on ALL mesh objects in your Blend file. Use with care'),\
148
245
if not Draw.PupBlock('Clean Selected Meshes...', pup_block):
152
CLEAN_VERTS_FREE = CLEAN_VERTS_FREE.val
153
CLEAN_EDGE_NOFACE = CLEAN_EDGE_NOFACE.val
154
CLEAN_EDGE_SMALL = CLEAN_EDGE_SMALL.val
155
CLEAN_FACE_PERIMETER = CLEAN_FACE_PERIMETER.val
156
CLEAN_FACE_SMALL = CLEAN_FACE_SMALL.val
248
CLEAN_VERTS_FREE= CLEAN_VERTS_FREE.val
249
CLEAN_EDGE_NOFACE= CLEAN_EDGE_NOFACE.val
250
CLEAN_EDGE_SMALL= CLEAN_EDGE_SMALL.val
251
CLEAN_FACE_PERIMETER= CLEAN_FACE_PERIMETER.val
252
CLEAN_FACE_SMALL= CLEAN_FACE_SMALL.val
253
CLEAN_MATERIALS= CLEAN_MATERIALS.val
254
CLEAN_GROUP= CLEAN_GROUP.val
255
CLEAN_VWEIGHT= CLEAN_VWEIGHT.val
256
CLEAN_WEIGHT_NORMALIZE= CLEAN_WEIGHT_NORMALIZE.val
258
CLEAN_ALL_DATA= CLEAN_ALL_DATA.val
159
260
if is_editmode: Window.EditMode(0)
161
rem_face_count = rem_edge_count = rem_vert_count = 0
263
if CLEAN_GROUP or CLEAN_VWEIGHT or CLEAN_WEIGHT_NORMALIZE:
264
# For groups we need the objects linked to the mesh
265
meshes= [ob.getData(mesh=1) for ob in Object.Get() if ob.getType() == 'Mesh']
269
meshes= [ob.getData(mesh=1) for ob in obsel if ob.getType() == 'Mesh']
271
rem_face_count= rem_edge_count= rem_vert_count= rem_material_count= rem_group_count= rem_vweight_count= 0
163
273
for me in meshes:
164
274
if CLEAN_FACE_SMALL:
176
286
if CLEAN_VERTS_FREE:
177
287
rem_vert_count += rem_free_verts(me)
290
rem_material_count += rem_unused_materials(me)
292
if CLEAN_VWEIGHT or CLEAN_GROUP or CLEAN_WEIGHT_NORMALIZE:
293
groupNames, vWeightDict= meshWeight2Dict(me)
296
rem_vweight_count += rem_zero_weights(me, limit, groupNames, vWeightDict)
299
rem_group_count += rem_free_groups(me, groupNames, vWeightDict)
302
if CLEAN_WEIGHT_NORMALIZE:
303
normalize_vweight(me, groupNames, vWeightDict)
305
# Copy back to mesh vertex groups.
306
dict2MeshWeight(me, groupNames, vWeightDict)
179
309
if is_editmode: Window.EditMode(0)
180
Draw.PupMenu('Removed from ' + str(len(meshes)) +' Mesh(es)%t|' + 'Verts:' + str(rem_vert_count) + ' Edges:' + str(rem_edge_count) + ' Faces:' + str(rem_face_count))
310
stat_string= 'Removed from ' + str(len(meshes)) + ' Mesh(es)%t|'
312
if CLEAN_VERTS_FREE: stat_string+= 'Verts: %i|' % rem_edge_count
313
if CLEAN_EDGE_SMALL or CLEAN_EDGE_NOFACE: stat_string+= 'Edges: %i|' % rem_edge_count
314
if CLEAN_FACE_SMALL or CLEAN_FACE_PERIMETER: stat_string+= 'Faces: %i|' % rem_face_count
315
if CLEAN_MATERIALS: stat_string+= 'Materials: %i|' % rem_material_count
316
if CLEAN_VWEIGHT: stat_string+= 'VWeights: %i|' % rem_vweight_count
317
if CLEAN_GROUP: stat_string+= 'VGroups: %i|' % rem_group_count
319
Draw.PupMenu(stat_string)
182
322
if __name__ == '__main__':