Small update...
Bugfix, Wrongly waited colinear test that caused quads with co-linear edges to be made. Bugfix, Use active object even if unselected. Optimize, Skip further tests if face pair is above the quad error limit. Optimize, Faster edge/Face user dict creation ~10% Feature: Options to ignore VCols and UV's as delimetres.
This commit is contained in:
@@ -26,8 +26,7 @@ and will not join faces that have mis-matching data.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
from Blender import Object, Mathutils, Draw, Window, sys
|
from Blender import Scene, Object, Mathutils, Draw, Window, sys
|
||||||
import math
|
|
||||||
|
|
||||||
TRI_LIST = (0,1,2)
|
TRI_LIST = (0,1,2)
|
||||||
|
|
||||||
@@ -89,11 +88,10 @@ def isfaceCoLin(imagQuag):
|
|||||||
# Work out how different from 90 each edge is.
|
# Work out how different from 90 each edge is.
|
||||||
diff = 0
|
diff = 0
|
||||||
try:
|
try:
|
||||||
diff += abs(vecAngle(edgeVec1, edgeVec2) - 90)
|
diff = abs(vecAngle(edgeVec1, edgeVec2) - 90)
|
||||||
diff += abs(vecAngle(edgeVec2, edgeVec3) - 90)
|
diff = max(diff, abs(vecAngle(edgeVec2, edgeVec3) - 90))
|
||||||
diff += abs(vecAngle(edgeVec3, edgeVec4) - 90)
|
diff = max(diff, abs(vecAngle(edgeVec3, edgeVec4) - 90))
|
||||||
diff += abs(vecAngle(edgeVec4, edgeVec1) - 90)
|
diff = max(diff, abs(vecAngle(edgeVec4, edgeVec1) - 90))
|
||||||
|
|
||||||
except:
|
except:
|
||||||
return 1.0
|
return 1.0
|
||||||
|
|
||||||
@@ -101,7 +99,7 @@ def isfaceCoLin(imagQuag):
|
|||||||
if not diff:
|
if not diff:
|
||||||
return 0.0
|
return 0.0
|
||||||
|
|
||||||
return diff/360
|
return min(diff/180, 1.0)
|
||||||
|
|
||||||
|
|
||||||
# Meause the areas of the 2 possible ways of subdividing the imagined quad.
|
# Meause the areas of the 2 possible ways of subdividing the imagined quad.
|
||||||
@@ -151,7 +149,7 @@ def meshJoinFacesTest(f1, f2, V1FREE, V2FREE):
|
|||||||
|
|
||||||
|
|
||||||
# Measure how good a quad the 2 tris will make,
|
# Measure how good a quad the 2 tris will make,
|
||||||
def measureFacePair(f1, f2, f1free, f2free):
|
def measureFacePair(f1, f2, f1free, f2free, limit):
|
||||||
# Make a imaginary quad. from 2 tris into 4 verts
|
# Make a imaginary quad. from 2 tris into 4 verts
|
||||||
imagFace = meshJoinFacesTest(f1, f2, f1free, f2free)
|
imagFace = meshJoinFacesTest(f1, f2, f1free, f2free)
|
||||||
if imagFace is False:
|
if imagFace is False:
|
||||||
@@ -163,24 +161,29 @@ def measureFacePair(f1, f2, f1free, f2free):
|
|||||||
# each value will add to the measure value
|
# each value will add to the measure value
|
||||||
# and be between 0 and 1.0
|
# and be between 0 and 1.0
|
||||||
|
|
||||||
measure+= isfaceNoDiff(imagFace)
|
measure+= isfaceNoDiff(imagFace)/3
|
||||||
measure+= isfaceCoLin(imagFace)
|
if measure > limit: return False
|
||||||
measure+= isfaceConcave(imagFace)
|
measure+= isfaceCoLin(imagFace)/3
|
||||||
|
if measure > limit: return False
|
||||||
|
measure+= isfaceConcave(imagFace)/3
|
||||||
|
if measure > limit: return False
|
||||||
|
|
||||||
# For debugging.
|
|
||||||
'''
|
'''
|
||||||
|
# For debugging.
|
||||||
|
|
||||||
a1= isfaceNoDiff(imagFace)
|
a1= isfaceNoDiff(imagFace)
|
||||||
a2= isfaceCoLin(imagFace)
|
a2= isfaceCoLin(imagFace)
|
||||||
a3= isfaceConcave(imagFace)
|
a3= isfaceConcave(imagFace)
|
||||||
|
|
||||||
print 'a1 %f' % a1
|
#print 'a1 NODIFF %f' % a1
|
||||||
print 'a2 %f' % a2
|
#print 'a2 COLIN %f' % a2
|
||||||
print 'a3 %f' % a3
|
#print 'a3 CONCAVE %f' % a3
|
||||||
|
|
||||||
measure = a1+a2+a3
|
measure = (a1+a2+a3)/3
|
||||||
|
if measure > limit: return False
|
||||||
'''
|
'''
|
||||||
|
|
||||||
return [f1,f2, measure/3, f1free, f2free]
|
return [f1,f2, measure, f1free, f2free]
|
||||||
|
|
||||||
|
|
||||||
# We know the faces are good to join, simply execute the join
|
# We know the faces are good to join, simply execute the join
|
||||||
@@ -225,11 +228,7 @@ def meshJoinFaces(f1, f2, V1FREE, V2FREE, mesh):
|
|||||||
# remove the edge from accress the new quad
|
# remove the edge from accress the new quad
|
||||||
f2.v.pop(not V2FREE)
|
f2.v.pop(not V2FREE)
|
||||||
|
|
||||||
|
|
||||||
# DEBUG
|
|
||||||
if mesh.edges:
|
|
||||||
mesh.removeEdge(edgeVert1, edgeVert2)
|
mesh.removeEdge(edgeVert1, edgeVert2)
|
||||||
|
|
||||||
#return f2
|
#return f2
|
||||||
|
|
||||||
|
|
||||||
@@ -285,10 +284,8 @@ def compareFaceCol(f1, f2):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def sortPair(a,b):
|
|
||||||
return min(a,b), max(a,b)
|
|
||||||
|
|
||||||
def tri2quad(mesh, limit, selectedFacesOnly):
|
def tri2quad(mesh, limit, selectedFacesOnly, respectUVs, respectVCols):
|
||||||
print '\nStarting tri2quad for mesh: %s' % mesh.name
|
print '\nStarting tri2quad for mesh: %s' % mesh.name
|
||||||
print '\t...finding pairs'
|
print '\t...finding pairs'
|
||||||
time1 = sys.time() # Time the function
|
time1 = sys.time() # Time the function
|
||||||
@@ -303,25 +300,36 @@ def tri2quad(mesh, limit, selectedFacesOnly):
|
|||||||
else:
|
else:
|
||||||
faceList = [f for f in mesh.faces if len(f) == 3]
|
faceList = [f for f in mesh.faces if len(f) == 3]
|
||||||
|
|
||||||
|
# Set if applicable for this mesh.
|
||||||
|
has_face_uv= has_vert_col = False
|
||||||
|
if respectUVs:
|
||||||
has_face_uv = mesh.hasFaceUV()
|
has_face_uv = mesh.hasFaceUV()
|
||||||
|
if respectVCols:
|
||||||
has_vert_col = mesh.hasVertexColours()
|
has_vert_col = mesh.hasVertexColours()
|
||||||
|
|
||||||
|
|
||||||
# Build a list of edges and tris that use those edges.
|
# Build a list of edges and tris that use those edges.
|
||||||
|
# This is so its faster to find tri pairs.
|
||||||
edgeFaceUsers = {}
|
edgeFaceUsers = {}
|
||||||
for f in faceList:
|
for f in faceList:
|
||||||
i1,i2,i3 = f.v[0].index, f.v[1].index, f.v[2].index
|
|
||||||
ed1, ed2, ed3 = sortPair(i1, i2), sortPair(i2, i3), sortPair(i3, i1)
|
edkey1a= edkey3b= f.v[0].index
|
||||||
|
edkey1b= edkey2a= f.v[1].index
|
||||||
|
edkey2b= edkey3a= f.v[2].index
|
||||||
|
|
||||||
|
if edkey1a > edkey1b: edkey1a, edkey1b = edkey1b, edkey1a
|
||||||
|
if edkey2a > edkey2b: edkey2a, edkey2b = edkey2b, edkey2a
|
||||||
|
if edkey3a > edkey3b: edkey3a, edkey3b = edkey3b, edkey3a
|
||||||
|
|
||||||
# The second int in the tuple is the free vert, its easier to store then to work it out again.
|
# The second int in the tuple is the free vert, its easier to store then to work it out again.
|
||||||
try: edgeFaceUsers[ed1].append((f, 2))
|
try: edgeFaceUsers[edkey1a, edkey1b].append((f, 2))
|
||||||
except: edgeFaceUsers[ed1] = [(f, 2)]
|
except: edgeFaceUsers[edkey1a, edkey1b] = [(f, 2)]
|
||||||
|
|
||||||
try: edgeFaceUsers[ed2].append((f, 0))
|
try: edgeFaceUsers[edkey2a, edkey2b].append((f, 0))
|
||||||
except: edgeFaceUsers[ed2] = [(f, 0)]
|
except: edgeFaceUsers[edkey2a, edkey2b] = [(f, 0)]
|
||||||
|
|
||||||
try: edgeFaceUsers[ed3].append((f, 1))
|
try: edgeFaceUsers[edkey3a, edkey3b].append((f, 1))
|
||||||
except: edgeFaceUsers[ed3] = [(f, 1)]
|
except: edgeFaceUsers[edkey3a, edkey3b] = [(f, 1)]
|
||||||
|
|
||||||
|
|
||||||
edgeDoneCount = 0
|
edgeDoneCount = 0
|
||||||
@@ -339,8 +347,9 @@ def tri2quad(mesh, limit, selectedFacesOnly):
|
|||||||
else:
|
else:
|
||||||
# We can now store the qpair and measure
|
# We can now store the qpair and measure
|
||||||
# there eligability for becoming 1 quad.
|
# there eligability for becoming 1 quad.
|
||||||
pair = measureFacePair(f1, f2, f1free, f2free)
|
pair = measureFacePair(f1, f2, f1free, f2free, limit)
|
||||||
if pair is not False and pair[2] < limit: # Some terraible error
|
#if pair is not False and pair[2] < limit: # Some terraible error
|
||||||
|
if pair is not False: # False means its above the limit.
|
||||||
facePairLs.append(pair)
|
facePairLs.append(pair)
|
||||||
pairCount += 1
|
pairCount += 1
|
||||||
|
|
||||||
@@ -372,7 +381,7 @@ def tri2quad(mesh, limit, selectedFacesOnly):
|
|||||||
|
|
||||||
if not pIdx % 20:
|
if not pIdx % 20:
|
||||||
p = (0.5 + ((float((len_facePairLs - (len_facePairLs - pIdx))))/len_facePairLs*0.5)) * 0.99
|
p = (0.5 + ((float((len_facePairLs - (len_facePairLs - pIdx))))/len_facePairLs*0.5)) * 0.99
|
||||||
Window.DrawProgressBar(p, 'Joining Face count: %i' % joinCount)
|
Window.DrawProgressBar(p, 'Joining Face count: %i of %i' % (joinCount, len_facePairLs))
|
||||||
draws +=1
|
draws +=1
|
||||||
|
|
||||||
# print 'Draws', draws
|
# print 'Draws', draws
|
||||||
@@ -385,10 +394,6 @@ def tri2quad(mesh, limit, selectedFacesOnly):
|
|||||||
if len(mesh.faces[fIdx]) < 3:
|
if len(mesh.faces[fIdx]) < 3:
|
||||||
mesh.faces.pop(fIdx)
|
mesh.faces.pop(fIdx)
|
||||||
|
|
||||||
# Was buggy in 2.4.alpha fixed now I think
|
|
||||||
#mesh.faces[0].sel = 0
|
|
||||||
|
|
||||||
|
|
||||||
if joinCount:
|
if joinCount:
|
||||||
print "tri2quad time for %s: %s joined %s tri's into quads" % (mesh.name, sys.time()-time1, joinCount)
|
print "tri2quad time for %s: %s joined %s tri's into quads" % (mesh.name, sys.time()-time1, joinCount)
|
||||||
|
|
||||||
@@ -407,8 +412,14 @@ def error(str):
|
|||||||
Draw.PupMenu('ERROR%t|'+str)
|
Draw.PupMenu('ERROR%t|'+str)
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
scn= Scene.GetCurrent()
|
||||||
|
|
||||||
#selection = Object.Get()
|
#selection = Object.Get()
|
||||||
selection = Object.GetSelected()
|
selection = Object.GetSelected()
|
||||||
|
actob = scn.getActiveObject()
|
||||||
|
if not actob.sel:
|
||||||
|
selection.append(actob)
|
||||||
|
|
||||||
if len(selection) is 0:
|
if len(selection) is 0:
|
||||||
error('No object selected')
|
error('No object selected')
|
||||||
return
|
return
|
||||||
@@ -422,14 +433,20 @@ def main():
|
|||||||
|
|
||||||
# Create the variables.
|
# Create the variables.
|
||||||
selectedFacesOnly = Draw.Create(1)
|
selectedFacesOnly = Draw.Create(1)
|
||||||
|
respectUVs = Draw.Create(1)
|
||||||
|
respectVCols = Draw.Create(1)
|
||||||
limit = Draw.Create(25)
|
limit = Draw.Create(25)
|
||||||
|
|
||||||
|
|
||||||
pup_block = [\
|
pup_block = [\
|
||||||
('Selected Faces Only', selectedFacesOnly, 'Use only selected faces from all selected meshes.'),\
|
('Selected Faces Only', selectedFacesOnly, 'Use only selected faces from all selected meshes.'),\
|
||||||
|
('UV Delimit', respectUVs, 'Only join pairs that have matching UVs on the joining edge.'),\
|
||||||
|
('VCol Delimit', respectVCols, 'Only join pairs that have matching Vert Colors on the joining edge.'),\
|
||||||
('Limit: ', limit, 1, 100, 'A higher value will join more tris to quads, even if the quads are not perfect.'),\
|
('Limit: ', limit, 1, 100, 'A higher value will join more tris to quads, even if the quads are not perfect.'),\
|
||||||
]
|
]
|
||||||
selectedFacesOnly = selectedFacesOnly.val
|
selectedFacesOnly = selectedFacesOnly.val
|
||||||
|
respectUVs = respectUVs.val
|
||||||
|
respectVCols = respectVCols.val
|
||||||
limit = limit.val
|
limit = limit.val
|
||||||
|
|
||||||
if not Draw.PupBlock('Tri2Quad for %i mesh object(s)' % len(meshDict), pup_block):
|
if not Draw.PupBlock('Tri2Quad for %i mesh object(s)' % len(meshDict), pup_block):
|
||||||
@@ -443,7 +460,7 @@ def main():
|
|||||||
|
|
||||||
for ob in meshDict.itervalues():
|
for ob in meshDict.itervalues():
|
||||||
mesh = ob.getData()
|
mesh = ob.getData()
|
||||||
tri2quad(mesh, limit, selectedFacesOnly)
|
tri2quad(mesh, limit, selectedFacesOnly, respectUVs, respectVCols)
|
||||||
if is_editmode: Window.EditMode(1)
|
if is_editmode: Window.EditMode(1)
|
||||||
|
|
||||||
# Dont run when importing
|
# Dont run when importing
|
||||||
|
|||||||
Reference in New Issue
Block a user