forked from bartvdbraak/blender
Made editmesh remove_doubles work with vertex colours,
added vertex colour support to BPyMesh_redux polygon reducing function.
This commit is contained in:
parent
8899af40fd
commit
b0c5df16f6
@ -1,6 +1,6 @@
|
||||
import Blender
|
||||
Vector= Blender.Mathutils.Vector
|
||||
Ang= Blender.Mathutils.AngleBetweenVecs
|
||||
# Ang= Blender.Mathutils.AngleBetweenVecs
|
||||
LineIntersect= Blender.Mathutils.LineIntersect
|
||||
import BPyMesh
|
||||
|
||||
@ -19,6 +19,26 @@ def ed_key(ed):
|
||||
if i1<i2: return i1,i2
|
||||
return i2,i1
|
||||
|
||||
def col_key(col):
|
||||
return col.r, col.g, col.b
|
||||
|
||||
class collapseEdge(object):
|
||||
__slots__ = 'length', 'key', 'faces', 'collapse_loc', 'v1', 'v2','uv1', 'uv2', 'col1', 'col2', 'collapse_weight'
|
||||
def __init__(self, ed):
|
||||
self.key= ed_key(ed)
|
||||
self.length= ed.length
|
||||
self.faces= []
|
||||
self.v1= ed.v1
|
||||
self.v2= ed.v2
|
||||
self.uv1= []
|
||||
self.uv2= []
|
||||
self.col1= []
|
||||
self.col2= []
|
||||
self.collapse_loc= None # new collapse location.
|
||||
self.collapse_weight= self.length * (1+ ((ed.v1.no-ed.v2.no).length**2))
|
||||
|
||||
|
||||
|
||||
def redux(ob, factor=0.5):
|
||||
me= ob.getData(mesh=1)
|
||||
# BUG MUST REMOVE GROUPS
|
||||
@ -28,73 +48,64 @@ def redux(ob, factor=0.5):
|
||||
OLD_MESH_MODE= Blender.Mesh.Mode()
|
||||
Blender.Mesh.Mode(Blender.Mesh.SelectModes.VERTEX)
|
||||
|
||||
faceUV= me.faceUV
|
||||
|
||||
target_face_count= int(len(me.faces) * factor)
|
||||
# % of the collapseable faces to collapse per pass.
|
||||
collapse_per_pass= 0.333 # between 0.1 - lots of small nibbles, slow but high q. and 0.9 - big passes and faster.
|
||||
|
||||
|
||||
for v in me.verts:
|
||||
v.hide=0
|
||||
|
||||
while target_face_count <= len(me.faces):
|
||||
BPyMesh.meshCalcNormals(me)
|
||||
|
||||
#groupNames, vWeightDict= meshWeight2Dict(act_me)
|
||||
|
||||
# Select all verts, de-select as you collapse.
|
||||
for v in me.verts:
|
||||
v.sel=0
|
||||
|
||||
# Store new locations for collapsed edges here
|
||||
edge_new_locations= [None] * len(me.edges)
|
||||
|
||||
|
||||
# For collapsing uv coords, we need to store the uv coords of edges as used by face users.
|
||||
# each dict key is min/max edge vert indicies
|
||||
# each value is a tuple of 2 lists, v1 uv's and v2 uvs. and the last list is for faces.
|
||||
# VALUE
|
||||
# edge_key : [faces, v1uvs, v2uvs]
|
||||
# faces, v1uvs, v2uvs - are all in synk.
|
||||
edge_faces_and_uvs= dict([ (ed_key(ed), ([], [], [])) for ed in me.edges])
|
||||
# Backup colors
|
||||
if me.faceUV:
|
||||
orig_texface= [[(uv_key(f.uv[i]), col_key(f.col[i])) for i in xrange(len(f.v))] for f in me.faces]
|
||||
|
||||
collapse_edges= [collapseEdge(ed) for ed in me.edges]
|
||||
collapse_edges_dict= dict( [(ce.key, ce) for ce in collapse_edges] )
|
||||
del ed
|
||||
|
||||
# Store verts edges.
|
||||
vert_ed_users= [[] for i in xrange(len(me.verts))]
|
||||
for ed in me.edges:
|
||||
vert_ed_users[ed.v1.index].append(ed)
|
||||
vert_ed_users[ed.v2.index].append(ed)
|
||||
for ced in collapse_edges:
|
||||
vert_ed_users[ced.v1.index].append(ced)
|
||||
vert_ed_users[ced.v2.index].append(ced)
|
||||
|
||||
# Store face users
|
||||
vert_face_users= [[] for i in xrange(len(me.verts))]
|
||||
|
||||
for f in me.faces:
|
||||
for ii, f in enumerate(me.faces):
|
||||
f_v= f.v
|
||||
if faceUV:
|
||||
tex_keys= orig_texface[ii]
|
||||
|
||||
#f.uvSel= uvs
|
||||
for i, v1 in enumerate(f.v):
|
||||
for i, v1 in enumerate(f_v):
|
||||
vert_face_users[v1.index].append( (i,f) )
|
||||
|
||||
# add the uv coord to the vert
|
||||
v2 = f.v[i-1]
|
||||
v2 = f_v[i-1]
|
||||
i1= v1.index
|
||||
i2= v2.index
|
||||
|
||||
if i1>i2:
|
||||
edge_face_list, edge_v2_uvs, edge_v1_uvs= edge_faces_and_uvs[i2,i1]
|
||||
else:
|
||||
edge_face_list, edge_v1_uvs, edge_v2_uvs= edge_faces_and_uvs[i1,i2]
|
||||
|
||||
edge_face_list.append(f)
|
||||
if me.faceUV:
|
||||
edge_v1_uvs.append( uv_key(f.uv[i ]) )
|
||||
edge_v2_uvs.append( uv_key(f.uv[i-1]) )
|
||||
if i1>i2: ced= collapse_edges_dict[i2,i1]
|
||||
else: ced= collapse_edges_dict[i1,i2]
|
||||
|
||||
ced.faces.append(f)
|
||||
if faceUV:
|
||||
ced.uv1.append( tex_keys[i][0] )
|
||||
ced.uv2.append( tex_keys[i-1][0] )
|
||||
|
||||
ced.col1.append( tex_keys[i][1] )
|
||||
ced.col2.append( tex_keys[i-1][1] )
|
||||
|
||||
'''
|
||||
face_normals= [f.no for f in me.faces]
|
||||
face_areas= [f.area for f in me.faces]
|
||||
|
||||
# Best method, no quick hacks here
|
||||
# Best method, no quick hacks here, Correction. Should be the best but needs tweaks.
|
||||
def ed_test_collapse_error(ed):
|
||||
|
||||
i1= ed.v1.index
|
||||
@ -130,14 +141,13 @@ def redux(ob, factor=0.5):
|
||||
pass
|
||||
# This is very arbirary, feel free to modify
|
||||
return angle_diff * ((ed.v1.no - ed.v2.no).length * ed.length)
|
||||
|
||||
'''
|
||||
|
||||
# Store egde lengths - Used
|
||||
# edge_lengths= [ed.length for ed in me.edges]
|
||||
|
||||
# Better method of weighting - edge length * normal difference.
|
||||
edge_lengths= [ed.length * (1 + ((ed.v1.no-ed.v2.no).length**2) ) for ed in me.edges]
|
||||
# edge_lengths= [ed.length * (1 + ((ed.v1.no-ed.v2.no).length**2) ) for ed in me.edges]
|
||||
|
||||
# tricky but somehow looks crap!!
|
||||
#edge_lengths= [ed_test_collapse_error(ed) for ed in me.edges]
|
||||
@ -148,84 +158,97 @@ def redux(ob, factor=0.5):
|
||||
|
||||
# BOUNDRY CHECKING AND WEIGHT EDGES. CAN REMOVE
|
||||
# Now we know how many faces link to an edge. lets get all the boundry verts
|
||||
verts_boundry= [0]*len(me.verts)
|
||||
for ed_idxs, faces_and_uvs in edge_faces_and_uvs.iteritems():
|
||||
if len(faces_and_uvs[0]) < 2:
|
||||
verts_boundry[ed_idxs[0]]= 1
|
||||
verts_boundry[ed_idxs[1]]= 1
|
||||
verts_boundry= [1]*len(me.verts)
|
||||
#for ed_idxs, faces_and_uvs in edge_faces_and_uvs.iteritems():
|
||||
for ced in collapse_edges:
|
||||
if len(ced.faces) < 2:
|
||||
verts_boundry[ced.key[0]]= 2
|
||||
verts_boundry[ced.key[1]]= 2
|
||||
|
||||
for i, ed in enumerate(me.edges):
|
||||
if verts_boundry[ed.v1.index] != verts_boundry[ed.v2.index]:
|
||||
for ced in collapse_edges:
|
||||
if verts_boundry[ced.v1.index] != verts_boundry[ced.v2.index]:
|
||||
# Edge has 1 boundry and 1 non boundry vert. weight higher
|
||||
edge_lengths[i]*=2
|
||||
ced.collapse_weight*=2
|
||||
|
||||
|
||||
vert_collapsed= verts_boundry
|
||||
del verts_boundry
|
||||
# END BOUNDRY. Can remove
|
||||
|
||||
# sort by edge length
|
||||
# sorted edge lengths
|
||||
edge_container_list= [ (edge_lengths[i], ed) for i, ed in enumerate(me.edges) ]
|
||||
edge_container_list.sort() # edges will be used for sorting
|
||||
# sort by collapse weight
|
||||
collapse_edges.sort(lambda ced1, ced2: cmp(ced1.collapse_weight, ced2.collapse_weight)) # edges will be used for sorting
|
||||
|
||||
# Make a list of the first half edges we can collapse,
|
||||
# these will better edges to remove.
|
||||
edge_container_list_collapse= []
|
||||
for length, ed in edge_container_list:
|
||||
#print 'Heho', len(me.faces)- current_removed_faces, target_face_count
|
||||
i= ed.index
|
||||
|
||||
v1= ed.v1
|
||||
v2= ed.v2
|
||||
collapse_count=0
|
||||
for ced in collapse_edges:
|
||||
v1= ced.v1
|
||||
v2= ced.v2
|
||||
# Use vert selections
|
||||
if v1.sel or v2.sel:
|
||||
if vert_collapsed[v1.index]==0 or vert_collapsed[v2.index]==0:
|
||||
pass
|
||||
|
||||
else:
|
||||
# Now we know the verts havnyt been collapsed.
|
||||
v1.sel= v2.sel= 1 # Dont collapse again.
|
||||
edge_container_list_collapse.append((length, ed))
|
||||
vert_collapsed[v1.index]= vert_collapsed[v2.index]= 0 # Dont collapse again.
|
||||
collapse_count+=1
|
||||
|
||||
# Get a subset of the entire list- the first "collapse_per_pass", that are best to collapse.
|
||||
if len(edge_container_list_collapse) > 4:
|
||||
edge_container_list_collapse = edge_container_list_collapse[:int(len(edge_container_list_collapse)*collapse_per_pass)]
|
||||
|
||||
if collapse_count > 4:
|
||||
collapse_count = int(collapse_count*collapse_per_pass)
|
||||
|
||||
# We know edge_container_list_collapse can be removed.
|
||||
for length, ed in edge_container_list_collapse:
|
||||
#print 'Heho', len(me.faces)- current_removed_faces, target_face_count
|
||||
i= ed.index
|
||||
for ced in collapse_edges:
|
||||
collapse_count-=1
|
||||
if not collapse_count:
|
||||
break
|
||||
v1= ced.v1
|
||||
v2= ced.v2
|
||||
|
||||
v1= ed.v1
|
||||
v2= ed.v2
|
||||
|
||||
edge_face_list, edge_v2_uvs, edge_v1_uvs= edge_faces_and_uvs[ed_key(ed)]
|
||||
#edge_face_list, edge_v2_uvs, edge_v1_uvs= edge_faces_and_uvs[ed_key(ed)]
|
||||
#current_removed_faces += len(edge_face_list) # dosent work for quads.
|
||||
|
||||
if me.faceUV:
|
||||
for v, edge_my_uvs, edge_other_uvs in ((v2, edge_v1_uvs, edge_v2_uvs),(v1, edge_v2_uvs, edge_v1_uvs)):
|
||||
if faceUV:
|
||||
for v, edge_my_uvs, edge_other_uvs, edge_my_cols, edge_other_cols in ((v2, ced.uv1, ced.uv2, ced.col1, ced.col2),(v1, ced.uv2, ced.uv1, ced.col2, ced.col1)):
|
||||
for face_vert_index, f in vert_face_users[v.index]:
|
||||
# we have a face and
|
||||
uvk= uv_key(f.uv[face_vert_index])
|
||||
|
||||
uv_index= None
|
||||
uvk, colk = orig_texface[f.index][face_vert_index]
|
||||
# UV COORDS
|
||||
tex_index= None
|
||||
try:
|
||||
uv_index= edge_my_uvs.index(uvk)
|
||||
tex_index= edge_my_uvs.index(uvk)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if uv_index != None:
|
||||
if tex_index != None:
|
||||
# This face uses a uv in the collapsing face. - do a merge
|
||||
other_uv= edge_other_uvs[uv_index]
|
||||
other_uv= edge_other_uvs[tex_index]
|
||||
uv_vec= f.uv[face_vert_index]
|
||||
uv_vec.x= (uvk[0] + other_uv[0])*0.5
|
||||
uv_vec.y= (uvk[1] + other_uv[1])*0.5
|
||||
|
||||
# TEXFACE COLOURS
|
||||
#colk = col_key(f.col[face_vert_index])
|
||||
|
||||
tex_index= None
|
||||
try:
|
||||
tex_index= edge_my_cols.index(colk)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if tex_index != None:
|
||||
# Col
|
||||
other_col= edge_other_cols[tex_index]
|
||||
col_ob= f.col[face_vert_index]
|
||||
col_ob.r = int((colk[0] + other_col[0])*0.5)
|
||||
col_ob.g = int((colk[1] + other_col[1])*0.5)
|
||||
col_ob.b = int((colk[2] + other_col[2])*0.5)
|
||||
|
||||
# Collapse
|
||||
between= (v1.co + v2.co) * 0.5
|
||||
# new_location = between # Replace tricky code below
|
||||
|
||||
# Collect edges from the faces that use this edge- dont use these in the new collapsed ver locatioin calc
|
||||
exclude_edges= set()
|
||||
for f in edge_face_list:
|
||||
for f in ced.faces:
|
||||
for ii, v in enumerate(f.v):
|
||||
i1= v.index
|
||||
i2= f.v[ii-1].index
|
||||
@ -238,15 +261,15 @@ def redux(ob, factor=0.5):
|
||||
# make a normal thats no longer then the edge length
|
||||
nor= v1.no + v2.no
|
||||
nor.normalize()
|
||||
nor= nor*length
|
||||
nor= nor*ced.length
|
||||
|
||||
new_location= Vector()
|
||||
new_location_count =0
|
||||
|
||||
# make a line we can do intersection with.
|
||||
for v in (ed.v1, ed.v2):
|
||||
for v in (ced.v1, ced.v2):
|
||||
for ed_user in vert_ed_users[v.index]:
|
||||
if ed_user != ed and ed_key(ed_user) not in exclude_edges:
|
||||
if ed_user != ced and ed_user.key not in exclude_edges:
|
||||
ed_between= (ed_user.v1.co+ed_user.v2.co) * 0.5
|
||||
v1_scale= ed_between + ((ed_user.v1.co-ed_between) * 100)
|
||||
v2_scale= ed_between + ((ed_user.v2.co-ed_between) * 100)
|
||||
@ -259,33 +282,29 @@ def redux(ob, factor=0.5):
|
||||
# or, out new location is crazy and further away from the edge center then the edge length.
|
||||
if not new_location_count or\
|
||||
new_location.x!=new_location.x or\
|
||||
(new_location-between).length > (length/2):
|
||||
(new_location-between).length > (ced.length/2):
|
||||
new_location= between
|
||||
else:
|
||||
new_location= new_location * (1.0/new_location_count)
|
||||
new_location = (new_location + between) * 0.5
|
||||
|
||||
# NEW NEW LOCATUON
|
||||
# print 'loop', len(me.faces)
|
||||
# Store the collapse location to apply later
|
||||
edge_new_locations[i] = new_location
|
||||
|
||||
ced.collapse_loc = new_location
|
||||
|
||||
# Execute the collapse
|
||||
for i , ed in enumerate(me.edges):
|
||||
loc= edge_new_locations[i]
|
||||
if loc:
|
||||
v1= ed.v1
|
||||
v2= ed.v2
|
||||
v1.co= v2.co= loc
|
||||
|
||||
doubles= me.remDoubles(0.0001)
|
||||
# print 'doubles', doubles
|
||||
me= ob.getData(mesh=1)
|
||||
if doubles==0:
|
||||
for ced in collapse_edges:
|
||||
# Since the list is ordered we can stop once the first non collapsed edge if sound.
|
||||
if not ced.collapse_loc:
|
||||
break
|
||||
|
||||
# Cleanup.
|
||||
ced.v1.co= ced.v2.co= ced.collapse_loc
|
||||
|
||||
doubles= me.remDoubles(0.0001)
|
||||
me= ob.getData(mesh=1)
|
||||
if doubles==0: # should never happen.
|
||||
break
|
||||
|
||||
# Cleanup. BUGGY?
|
||||
'''
|
||||
vert_face_user_count= [0]*len(me.verts)
|
||||
for f in me.faces:
|
||||
@ -304,8 +323,9 @@ def main():
|
||||
Blender.Window.EditMode(0)
|
||||
scn= Blender.Scene.GetCurrent()
|
||||
active_ob= scn.getActiveObject()
|
||||
|
||||
t= Blender.sys.time()
|
||||
redux(active_ob, 0.5)
|
||||
print '%.4f' % (Blender.sys.time()-t)
|
||||
|
||||
if __name__=='__main__':
|
||||
main()
|
||||
|
@ -383,9 +383,12 @@ int removedoublesflag(short flag, float limit) /* return amount */
|
||||
efa->v2= efa->v3;
|
||||
efa->tf.uv[1][0] = efa->tf.uv[2][0];
|
||||
efa->tf.uv[1][1] = efa->tf.uv[2][1];
|
||||
efa->tf.col[1] = efa->tf.col[2];
|
||||
|
||||
efa->v3= efa->v4;
|
||||
efa->tf.uv[2][0] = efa->tf.uv[3][0];
|
||||
efa->tf.uv[2][1] = efa->tf.uv[3][1];
|
||||
efa->tf.col[2] = efa->tf.col[3];
|
||||
efa->v4= 0;
|
||||
test= 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user