2006-05-11 12:42:10 +00:00
|
|
|
import Blender
|
|
|
|
Vector= Blender.Mathutils.Vector
|
|
|
|
LineIntersect= Blender.Mathutils.LineIntersect
|
2006-05-14 18:28:27 +00:00
|
|
|
CrossVecs= Blender.Mathutils.CrossVecs
|
2006-05-11 12:42:10 +00:00
|
|
|
import BPyMesh
|
|
|
|
|
2006-05-14 18:28:27 +00:00
|
|
|
'''
|
2006-05-13 05:24:58 +00:00
|
|
|
try:
|
|
|
|
import psyco
|
|
|
|
psyco.full()
|
|
|
|
except:
|
|
|
|
pass
|
2006-05-14 18:28:27 +00:00
|
|
|
'''
|
2006-05-11 20:35:43 +00:00
|
|
|
|
2006-05-11 12:42:10 +00:00
|
|
|
def uv_key(uv):
|
|
|
|
return round(uv.x, 5), round(uv.y, 5)
|
|
|
|
|
|
|
|
def ed_key(ed):
|
|
|
|
i1= ed.v1.index
|
|
|
|
i2= ed.v2.index
|
|
|
|
if i1<i2: return i1,i2
|
|
|
|
return i2,i1
|
2006-05-13 12:36:48 +00:00
|
|
|
|
|
|
|
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.
|
2006-05-14 18:28:27 +00:00
|
|
|
|
|
|
|
# Basic weighting.
|
|
|
|
#self.collapse_weight= self.length * (1+ ((ed.v1.no-ed.v2.no).length**2))
|
2006-05-13 12:36:48 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
2006-05-11 12:42:10 +00:00
|
|
|
def redux(ob, factor=0.5):
|
|
|
|
me= ob.getData(mesh=1)
|
2006-05-13 17:50:47 +00:00
|
|
|
|
2006-05-11 12:42:10 +00:00
|
|
|
# BUG MUST REMOVE GROUPS
|
|
|
|
if factor>1.0 or factor<0.0 or len(me.faces)<4:
|
|
|
|
return
|
|
|
|
|
|
|
|
OLD_MESH_MODE= Blender.Mesh.Mode()
|
|
|
|
Blender.Mesh.Mode(Blender.Mesh.SelectModes.VERTEX)
|
|
|
|
|
2006-05-13 12:36:48 +00:00
|
|
|
faceUV= me.faceUV
|
|
|
|
|
2006-05-11 12:42:10 +00:00
|
|
|
target_face_count= int(len(me.faces) * factor)
|
|
|
|
# % of the collapseable faces to collapse per pass.
|
2006-05-14 18:28:27 +00:00
|
|
|
#collapse_per_pass= 0.333 # between 0.1 - lots of small nibbles, slow but high q. and 0.9 - big passes and faster.
|
2006-05-11 12:42:10 +00:00
|
|
|
collapse_per_pass= 0.333 # between 0.1 - lots of small nibbles, slow but high q. and 0.9 - big passes and faster.
|
|
|
|
|
2006-05-11 20:35:43 +00:00
|
|
|
for v in me.verts:
|
|
|
|
v.hide=0
|
2006-05-11 12:42:10 +00:00
|
|
|
while target_face_count <= len(me.faces):
|
|
|
|
BPyMesh.meshCalcNormals(me)
|
|
|
|
|
2006-05-13 17:50:47 +00:00
|
|
|
for v in me.verts:
|
|
|
|
v.sel= False
|
|
|
|
|
2006-05-13 12:36:48 +00:00
|
|
|
# Backup colors
|
2006-05-14 18:28:27 +00:00
|
|
|
if faceUV:
|
2006-05-13 12:36:48 +00:00
|
|
|
orig_texface= [[(uv_key(f.uv[i]), col_key(f.col[i])) for i in xrange(len(f.v))] for f in me.faces]
|
2006-05-11 12:42:10 +00:00
|
|
|
|
2006-05-13 12:36:48 +00:00
|
|
|
collapse_edges= [collapseEdge(ed) for ed in me.edges]
|
2006-05-14 18:28:27 +00:00
|
|
|
collapse_edges_dict= dict( [(ced.key, ced) for ced in collapse_edges] )
|
|
|
|
|
2006-05-11 12:42:10 +00:00
|
|
|
|
|
|
|
# Store verts edges.
|
|
|
|
vert_ed_users= [[] for i in xrange(len(me.verts))]
|
2006-05-13 12:36:48 +00:00
|
|
|
for ced in collapse_edges:
|
|
|
|
vert_ed_users[ced.v1.index].append(ced)
|
|
|
|
vert_ed_users[ced.v2.index].append(ced)
|
2006-05-11 12:42:10 +00:00
|
|
|
|
|
|
|
# Store face users
|
|
|
|
vert_face_users= [[] for i in xrange(len(me.verts))]
|
|
|
|
|
2006-05-14 18:28:27 +00:00
|
|
|
#face_perim= [0.0]* len(me.faces)
|
|
|
|
|
2006-05-13 12:36:48 +00:00
|
|
|
for ii, f in enumerate(me.faces):
|
|
|
|
f_v= f.v
|
|
|
|
if faceUV:
|
|
|
|
tex_keys= orig_texface[ii]
|
|
|
|
|
|
|
|
for i, v1 in enumerate(f_v):
|
2006-05-11 12:42:10 +00:00
|
|
|
vert_face_users[v1.index].append( (i,f) )
|
|
|
|
|
|
|
|
# add the uv coord to the vert
|
2006-05-13 12:36:48 +00:00
|
|
|
v2 = f_v[i-1]
|
2006-05-11 12:42:10 +00:00
|
|
|
i1= v1.index
|
|
|
|
i2= v2.index
|
|
|
|
|
2006-05-13 12:36:48 +00:00
|
|
|
if i1>i2: ced= collapse_edges_dict[i2,i1]
|
|
|
|
else: ced= collapse_edges_dict[i1,i2]
|
2006-05-11 12:42:10 +00:00
|
|
|
|
2006-05-13 12:36:48 +00:00
|
|
|
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] )
|
2006-05-14 18:28:27 +00:00
|
|
|
|
|
|
|
# PERIMITER
|
|
|
|
#face_perim[ii]+= ced.length
|
|
|
|
|
2006-05-11 12:42:10 +00:00
|
|
|
|
|
|
|
face_normals= [f.no for f in me.faces]
|
|
|
|
face_areas= [f.area for f in me.faces]
|
|
|
|
|
2006-05-14 18:28:27 +00:00
|
|
|
|
2006-05-13 12:36:48 +00:00
|
|
|
# Best method, no quick hacks here, Correction. Should be the best but needs tweaks.
|
2006-05-14 18:28:27 +00:00
|
|
|
def ed_test_collapse_error(ced):
|
|
|
|
Ang= Blender.Mathutils.AngleBetweenVecs
|
2006-05-11 12:42:10 +00:00
|
|
|
|
2006-05-14 18:28:27 +00:00
|
|
|
i1= ced.v1.index
|
|
|
|
i2= ced.v1.index
|
2006-05-11 12:42:10 +00:00
|
|
|
test_faces= set()
|
2006-05-14 18:28:27 +00:00
|
|
|
|
2006-05-11 12:42:10 +00:00
|
|
|
for i in (i1,i2):
|
2006-05-14 18:28:27 +00:00
|
|
|
for f in vert_face_users[i]:
|
|
|
|
test_faces.add(f[1].index)
|
2006-05-11 12:42:10 +00:00
|
|
|
|
2006-05-14 18:28:27 +00:00
|
|
|
for f in ced.faces:
|
|
|
|
test_faces.remove(f.index)
|
2006-05-11 12:42:10 +00:00
|
|
|
|
2006-05-14 18:28:27 +00:00
|
|
|
test_faces= tuple(test_faces) # keep order
|
2006-05-11 12:42:10 +00:00
|
|
|
|
2006-05-14 18:28:27 +00:00
|
|
|
# test_faces is now faces used by ed.v1 and ed.v2 that will not be removed in the collapse.
|
|
|
|
# orig_nos= [face_normals[i] for i in test_faces]
|
2006-05-11 12:42:10 +00:00
|
|
|
|
2006-05-14 18:28:27 +00:00
|
|
|
v1_orig= Vector(ced.v1.co)
|
|
|
|
v2_orig= Vector(ced.v2.co)
|
2006-05-11 12:42:10 +00:00
|
|
|
|
2006-05-14 18:28:27 +00:00
|
|
|
ced.v1.co= ced.v2.co= (v1_orig+v2_orig) * 0.5
|
2006-05-11 12:42:10 +00:00
|
|
|
|
2006-05-14 18:28:27 +00:00
|
|
|
new_nos= [me.faces[i].no for i in test_faces]
|
2006-05-11 12:42:10 +00:00
|
|
|
|
2006-05-14 18:28:27 +00:00
|
|
|
ced.v1.co= v1_orig
|
|
|
|
ced.v2.co= v2_orig
|
2006-05-11 12:42:10 +00:00
|
|
|
|
|
|
|
# now see how bad the normals are effected
|
|
|
|
angle_diff= 0
|
|
|
|
|
2006-05-14 18:28:27 +00:00
|
|
|
for ii, i in enumerate(test_faces):
|
2006-05-11 12:42:10 +00:00
|
|
|
try:
|
2006-05-14 18:28:27 +00:00
|
|
|
# can use perim, but area looks better.
|
|
|
|
angle_diff+= (Ang(face_normals[i], new_nos[ii])/180) * (1+(face_areas[i]/2)) # 4 is how much to influence area
|
2006-05-11 12:42:10 +00:00
|
|
|
except:
|
|
|
|
pass
|
2006-05-14 18:28:27 +00:00
|
|
|
|
2006-05-11 12:42:10 +00:00
|
|
|
# This is very arbirary, feel free to modify
|
2006-05-14 18:28:27 +00:00
|
|
|
return angle_diff
|
2006-05-11 12:42:10 +00:00
|
|
|
|
2006-05-14 18:28:27 +00:00
|
|
|
# We can calculate the weights on __init__ but this is higher qualuity.
|
|
|
|
for ced in collapse_edges:
|
|
|
|
ced.collapse_weight = ed_test_collapse_error(ced)
|
2006-05-11 12:42:10 +00:00
|
|
|
|
|
|
|
# Wont use the function again.
|
2006-05-14 18:28:27 +00:00
|
|
|
del ed_test_collapse_error
|
|
|
|
|
2006-05-11 12:42:10 +00:00
|
|
|
|
|
|
|
# BOUNDRY CHECKING AND WEIGHT EDGES. CAN REMOVE
|
|
|
|
# Now we know how many faces link to an edge. lets get all the boundry verts
|
2006-05-13 12:36:48 +00:00
|
|
|
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
|
2006-05-11 12:42:10 +00:00
|
|
|
|
2006-05-13 12:36:48 +00:00
|
|
|
for ced in collapse_edges:
|
|
|
|
if verts_boundry[ced.v1.index] != verts_boundry[ced.v2.index]:
|
2006-05-11 12:42:10 +00:00
|
|
|
# Edge has 1 boundry and 1 non boundry vert. weight higher
|
2006-05-13 12:36:48 +00:00
|
|
|
ced.collapse_weight*=2
|
|
|
|
|
|
|
|
|
|
|
|
vert_collapsed= verts_boundry
|
2006-05-11 12:42:10 +00:00
|
|
|
del verts_boundry
|
|
|
|
# END BOUNDRY. Can remove
|
|
|
|
|
2006-05-13 12:36:48 +00:00
|
|
|
# sort by collapse weight
|
|
|
|
collapse_edges.sort(lambda ced1, ced2: cmp(ced1.collapse_weight, ced2.collapse_weight)) # edges will be used for sorting
|
2006-05-11 12:42:10 +00:00
|
|
|
|
|
|
|
# Make a list of the first half edges we can collapse,
|
|
|
|
# these will better edges to remove.
|
2006-05-13 12:36:48 +00:00
|
|
|
collapse_count=0
|
|
|
|
for ced in collapse_edges:
|
|
|
|
v1= ced.v1
|
|
|
|
v2= ced.v2
|
2006-05-11 12:42:10 +00:00
|
|
|
# Use vert selections
|
2006-05-13 12:36:48 +00:00
|
|
|
if vert_collapsed[v1.index]==0 or vert_collapsed[v2.index]==0:
|
2006-05-11 12:42:10 +00:00
|
|
|
pass
|
|
|
|
else:
|
|
|
|
# Now we know the verts havnyt been collapsed.
|
2006-05-13 12:36:48 +00:00
|
|
|
vert_collapsed[v1.index]= vert_collapsed[v2.index]= 0 # Dont collapse again.
|
|
|
|
collapse_count+=1
|
2006-05-11 12:42:10 +00:00
|
|
|
|
|
|
|
# Get a subset of the entire list- the first "collapse_per_pass", that are best to collapse.
|
2006-05-13 12:36:48 +00:00
|
|
|
if collapse_count > 4:
|
|
|
|
collapse_count = int(collapse_count*collapse_per_pass)
|
2006-05-11 12:42:10 +00:00
|
|
|
|
|
|
|
# We know edge_container_list_collapse can be removed.
|
2006-05-13 12:36:48 +00:00
|
|
|
for ced in collapse_edges:
|
|
|
|
collapse_count-=1
|
|
|
|
if not collapse_count:
|
|
|
|
break
|
|
|
|
v1= ced.v1
|
|
|
|
v2= ced.v2
|
2006-05-11 12:42:10 +00:00
|
|
|
|
2006-05-13 12:36:48 +00:00
|
|
|
#edge_face_list, edge_v2_uvs, edge_v1_uvs= edge_faces_and_uvs[ed_key(ed)]
|
2006-05-11 12:42:10 +00:00
|
|
|
#current_removed_faces += len(edge_face_list) # dosent work for quads.
|
|
|
|
|
2006-05-13 12:36:48 +00:00
|
|
|
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)):
|
2006-05-11 12:42:10 +00:00
|
|
|
for face_vert_index, f in vert_face_users[v.index]:
|
2006-05-13 12:36:48 +00:00
|
|
|
uvk, colk = orig_texface[f.index][face_vert_index]
|
|
|
|
# UV COORDS
|
|
|
|
tex_index= None
|
2006-05-11 12:42:10 +00:00
|
|
|
try:
|
2006-05-13 12:36:48 +00:00
|
|
|
tex_index= edge_my_uvs.index(uvk)
|
2006-05-11 12:42:10 +00:00
|
|
|
except ValueError:
|
|
|
|
pass
|
|
|
|
|
2006-05-13 12:36:48 +00:00
|
|
|
if tex_index != None:
|
2006-05-11 12:42:10 +00:00
|
|
|
# This face uses a uv in the collapsing face. - do a merge
|
2006-05-13 12:36:48 +00:00
|
|
|
other_uv= edge_other_uvs[tex_index]
|
2006-05-11 12:42:10 +00:00
|
|
|
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
|
2006-05-13 12:36:48 +00:00
|
|
|
|
|
|
|
# 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)
|
2006-05-11 12:42:10 +00:00
|
|
|
|
|
|
|
# Collapse
|
|
|
|
between= (v1.co + v2.co) * 0.5
|
2006-05-14 18:28:27 +00:00
|
|
|
# new_location = between # Replace tricky code below. this code predicts the best collapse location.
|
2006-05-11 12:42:10 +00:00
|
|
|
|
2006-05-14 18:28:27 +00:00
|
|
|
# Make lines at right angles to the normals- these 2 lines will intersect and be
|
|
|
|
# the point of collapsing.
|
|
|
|
cv1= CrossVecs(v1.no, CrossVecs(v1.no, v1.co-v2.co))
|
|
|
|
cv2= CrossVecs(v2.no, CrossVecs(v2.no, v2.co-v1.co))
|
|
|
|
cv1= cv1* ced.length*2 # Enlarge so we know they intersect.
|
|
|
|
cv2= cv2* ced.length*2
|
2006-05-11 12:42:10 +00:00
|
|
|
|
2006-05-14 18:28:27 +00:00
|
|
|
line_xs= LineIntersect(v1.co, v1.co+cv1, v2.co, v2.co+cv2)
|
|
|
|
if line_xs:
|
|
|
|
new_location = (line_xs[0]+line_xs[1]) * 0.5
|
|
|
|
if new_location.x!=new_location.x or\
|
|
|
|
(new_location-between).length > (ced.length/2):
|
2006-05-13 17:50:47 +00:00
|
|
|
new_location= between
|
2006-05-11 12:42:10 +00:00
|
|
|
else:
|
2006-05-14 18:28:27 +00:00
|
|
|
new_location= between
|
2006-05-13 05:24:58 +00:00
|
|
|
|
2006-05-13 12:36:48 +00:00
|
|
|
ced.collapse_loc = new_location
|
2006-05-14 18:28:27 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
2006-05-13 12:36:48 +00:00
|
|
|
|
2006-05-11 12:42:10 +00:00
|
|
|
# Execute the collapse
|
2006-05-13 12:36:48 +00:00
|
|
|
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
|
2006-05-13 17:50:47 +00:00
|
|
|
ced.v1.sel= ced.v2.sel= True
|
2006-05-13 12:36:48 +00:00
|
|
|
ced.v1.co= ced.v2.co= ced.collapse_loc
|
2006-05-11 12:42:10 +00:00
|
|
|
|
2006-05-11 20:35:43 +00:00
|
|
|
doubles= me.remDoubles(0.0001)
|
2006-05-11 12:42:10 +00:00
|
|
|
me= ob.getData(mesh=1)
|
2006-05-13 12:36:48 +00:00
|
|
|
if doubles==0: # should never happen.
|
2006-05-11 20:35:43 +00:00
|
|
|
break
|
2006-05-11 12:42:10 +00:00
|
|
|
|
2006-05-13 12:36:48 +00:00
|
|
|
# Cleanup. BUGGY?
|
2006-05-11 20:35:43 +00:00
|
|
|
'''
|
2006-05-11 12:42:10 +00:00
|
|
|
vert_face_user_count= [0]*len(me.verts)
|
|
|
|
for f in me.faces:
|
|
|
|
for v in f.v:
|
|
|
|
vert_face_user_count[v.index] +=1
|
|
|
|
|
|
|
|
del_verts= [i for i in xrange(len(me.verts)) if not vert_face_user_count[i]]
|
|
|
|
me.verts.delete( del_verts )
|
2006-05-11 20:35:43 +00:00
|
|
|
'''
|
2006-05-11 12:42:10 +00:00
|
|
|
me.update()
|
|
|
|
Blender.Mesh.Mode(OLD_MESH_MODE)
|
|
|
|
|
|
|
|
|
|
|
|
# Example usage
|
|
|
|
def main():
|
|
|
|
Blender.Window.EditMode(0)
|
|
|
|
scn= Blender.Scene.GetCurrent()
|
|
|
|
active_ob= scn.getActiveObject()
|
2006-05-13 12:36:48 +00:00
|
|
|
t= Blender.sys.time()
|
2006-05-11 12:42:10 +00:00
|
|
|
redux(active_ob, 0.5)
|
2006-05-13 12:36:48 +00:00
|
|
|
print '%.4f' % (Blender.sys.time()-t)
|
2006-05-11 12:42:10 +00:00
|
|
|
|
|
|
|
if __name__=='__main__':
|
|
|
|
main()
|