-> New face weld code for FLT importer

Replaced the use of remove doubles with some new custom 'face welding'
code that wil clean up meshes on import.This will not remove any faces
or do any edge collapse and so is a more suitable method of cleaning
up imported meshes. This same code could potentially be of use when
importing other formats as well (such as DXF...)
This commit is contained in:
Geoffrey Bantle 2008-03-26 15:59:56 +00:00
parent 2af78ac944
commit b46bad83bf

@ -620,10 +620,12 @@ class VertexPalette(Node):
def parse(self): # Run once per import
Node.parse(self)
class InterNode(Node):
def __init__(self):
self.object = None
self.mesh = None
self.swapmesh = None
self.hasMesh = False
self.faceLs= []
self.matrix = None
@ -638,6 +640,280 @@ class InterNode(Node):
self.uvlayers[mask] = False
mask = mask / 2
#######################################################
## Begin Remove Doubles Replacement ##
#######################################################
def __xvertsort(self,__a,__b):
(__vert, __x1) = __a
(__vert2,__x2) = __b
if __x1 > __x2:
return 1
elif __x1 < __x2:
return -1
return 0
def __calcFaceNorm(self,__face):
if len(__face) == 3:
return Blender.Mathutils.TriangleNormal(__face[0].co, __face[1].co, __face[2].co)
elif len(__face) == 4:
return Blender.Mathutils.QuadNormal(__face[0].co, __face[1].co, __face[2].co, __face[3].co)
def __replaceFaceVert(self,__weldface, __oldvert, __newvert):
__index = None
for __i, __v in enumerate(__weldface):
if __v == __oldvert:
__index = __i
break
__weldface[__index] = __newvert
def __matchEdge(self,__weldmesh, __edge1, __edge2):
if __edge1[0] in __weldmesh['Vertex Disk'][__edge2[1]] and __edge1[1] in __weldmesh['Vertex Disk'][__edge2[0]]:
return True
return False
#have to compare original faces!
def __faceWinding(self, __weldmesh, __face1, __face2):
__f1edges = list()
__f2edges = list()
__f1edges.append((__face1.verts[0], __face1.verts[1]))
__f1edges.append((__face1.verts[1], __face1.verts[2]))
if len(__face1.verts) == 3:
__f1edges.append((__face1.verts[2], __face1.verts[0]))
else:
__f1edges.append((__face1.verts[2], __face1.verts[3]))
__f1edges.append((__face1.verts[3], __face1.verts[0]))
__f2edges.append((__face2.verts[0], __face2.verts[1]))
__f2edges.append((__face2.verts[1], __face2.verts[2]))
if len(__face2.verts) == 3:
__f2edges.append((__face2.verts[2], __face2.verts[0]))
else:
__f2edges.append((__face2.verts[2], __face2.verts[3]))
__f2edges.append((__face2.verts[3], __face2.verts[0]))
#find a matching edge
for __edge1 in __f1edges:
for __edge2 in __f2edges:
if self.__matchEdge(__weldmesh, __edge1, __edge2): #no more tests nessecary
return True
return False
def __floatcompare(self, __f1, __f2):
epsilon = 0.01
if ((__f1 + epsilon) > __f2) and ((__f1 - epsilon) < __f2):
return True
return False
def __testFace(self,__weldmesh,__v1face, __v2face, __v1bface, __v2bface):
limit = 0.01
__matchvert = None
#frst test (for real this time!). Are the faces the same face?
if __v1face == __v2face:
return False
#first test: Do the faces possibly geometrically share more than two vertices? we should be comparing original faces for this? - Yes.....
__match = 0
for __vert in __v1bface.verts:
for __vert2 in __v2bface.verts:
if (abs(__vert.co[0] - __vert2.co[0]) <= limit) and (abs(__vert.co[1] - __vert2.co[1]) <= limit) and (abs(__vert.co[2] - __vert2.co[2]) <= limit): #this needs to be fixed!
#if __vert2 in __weldmesh['Vertex Disk'][__vert] or __vert == __vert2:
__match += 1
__matchvert = __vert2
#avoid faces sharing more than two verts
if __match > 2:
return False
#consistent winding for face normals
if not self.__faceWinding(__weldmesh, __v1bface, __v2bface):
return False
#second test: Compatible normals.Anything beyond almost exact opposite is 'ok'
__v1facenorm = self.__calcFaceNorm(__v1face)
__v2facenorm = self.__calcFaceNorm(__v2face)
#dont even mess with zero length faces
if __v1facenorm.length < limit:
return False
if __v2facenorm.length < limit:
return False
__v1facenorm.normalize()
__v2facenorm.normalize()
__v2facenorm = __v2facenorm.negate()
if self.__floatcompare(__v1facenorm[0], __v2facenorm[0]) and self.__floatcompare(__v1facenorm[1], __v2facenorm[1]) and self.__floatcompare(__v1facenorm[2], __v2facenorm[2]):
return False
#next test: dont weld a subface to a non-subface!
if __v1bface.getProperty("FLT_SFLEVEL") != __v2bface.getProperty("FLT_SFLEVEL"):
return False
#final test: edge test - We dont want to create a non-manifold edge through our weld operation
return True
def __copyFaceData(self, __source, __target):
#copy vcolor layers.
__actColLayer = self.mesh.activeColorLayer
for __colorlayer in self.mesh.getColorLayerNames():
self.mesh.activeColorLayer = __colorlayer
for __i, __col in enumerate(__source.col):
__target.col[__i].r = __col.r
__target.col[__i].g = __col.g
__target.col[__i].b = __col.b
self.mesh.activeColorLayer = __actColLayer
#copy uv layers.
__actUVLayer = self.mesh.activeUVLayer
for __uvlayer in self.mesh.getUVLayerNames():
self.mesh.activeUVLayer = __uvlayer
__target.image = __source.image
__target.mode = __source.mode
__target.smooth = __source.smooth
__target.transp = __source.transp
for __i, __uv in enumerate(__source.uv):
__target.uv[__i][0] = __uv[0]
__target.uv[__i][1] = __uv[1]
self.mesh.activeUVLayer = __actUVLayer
#copy property layers
for __property in self.mesh.faces.properties:
__target.setProperty(__property, __source.getProperty(__property))
def findDoubles(self):
limit = 0.01
sortblock = list()
double = dict()
for vert in self.mesh.verts:
double[vert] = None
sortblock.append((vert, vert.co[0] + vert.co[1] + vert.co[2]))
sortblock.sort(self.__xvertsort)
a = 0
while a < len(self.mesh.verts):
(vert,xsort) = sortblock[a]
b = a+1
if not double[vert]:
while b < len(self.mesh.verts):
(vert2, xsort2) = sortblock[b]
if not double[vert2]:
#first test, simple distance
if (xsort2 - xsort) > limit:
break
#second test, more expensive
if (abs(vert.co[0] - vert2.co[0]) <= limit) and (abs(vert.co[1] - vert2.co[1]) <= limit) and (abs(vert.co[2] - vert2.co[2]) <= limit):
double[vert2] = vert
b+=1
a+=1
return double
def buildWeldMesh(self):
weldmesh = dict()
weldmesh['Vertex Disk'] = dict() #this is geometric adjacency
weldmesh['Vertex Faces'] = dict() #topological adjacency
#find the doubles for this mesh
double = self.findDoubles()
for vert in self.mesh.verts:
weldmesh['Vertex Faces'][vert] = list()
#create weld faces
weldfaces = list()
originalfaces = list()
for face in self.mesh.faces:
weldface = list()
for vert in face.verts:
weldface.append(vert)
weldfaces.append(weldface)
originalfaces.append(face)
for i, weldface in enumerate(weldfaces):
for vert in weldface:
weldmesh['Vertex Faces'][vert].append(i)
weldmesh['Weld Faces'] = weldfaces
weldmesh['Original Faces'] = originalfaces
#Now we need to build the vertex disk data. first we do just the 'target' vertices
for vert in self.mesh.verts:
if not double[vert]: #its a target
weldmesh['Vertex Disk'][vert] = list()
for vert in self.mesh.verts:
if double[vert]: #its a double
weldmesh['Vertex Disk'][double[vert]].append(vert)
#Now we need to create the disk information for the remaining vertices
targets = weldmesh['Vertex Disk'].keys()
for target in targets:
for doublevert in weldmesh['Vertex Disk'][target]:
weldmesh['Vertex Disk'][doublevert] = [target]
for othervert in weldmesh['Vertex Disk'][target]:
if othervert != doublevert:
weldmesh['Vertex Disk'][doublevert].append(othervert)
return weldmesh
def weldFuseFaces(self,weldmesh):
#slight modification here: we need to walk around the mesh as many times as it takes to have no more matches
done = 0
while not done:
done = 1
for windex, weldface in enumerate(weldmesh['Weld Faces']):
for vertex in weldface:
#we walk around the faces of the doubles of this vertex and if possible, we weld them.
for doublevert in weldmesh['Vertex Disk'][vertex]:
removeFaces = list() #list of faces to remove from doubleverts face list
for doublefaceindex in weldmesh['Vertex Faces'][doublevert]:
doubleface = weldmesh['Weld Faces'][doublefaceindex]
oface1 = self.mesh.faces[windex]
oface2 = self.mesh.faces[doublefaceindex]
ok = self.__testFace(weldmesh, weldface, doubleface, oface1, oface2)
if ok:
done = 0
removeFaces.append(doublefaceindex)
self.__replaceFaceVert(doubleface, doublevert, vertex)
for doublefaceindex in removeFaces:
weldmesh['Vertex Faces'][doublevert].remove(doublefaceindex)
#old faces first
oldindices = list()
for face in self.mesh.faces:
oldindices.append(face.index)
#make our new faces.
newfaces = list()
for weldface in weldmesh['Weld Faces']:
newfaces.append(weldface)
newindices = self.mesh.faces.extend(newfaces, indexList=True, ignoreDups=True)
#copy custom data over
for i, newindex in enumerate(newindices):
try:
self.__copyFaceData(self.mesh.faces[oldindices[i]], self.mesh.faces[newindex])
except:
print "warning, could not copy face data!"
#delete the old faces
self.mesh.faces.delete(1, oldindices)
#Clean up stray vertices
vertuse = dict()
for vert in self.mesh.verts:
vertuse[vert] = 0
for face in self.mesh.faces:
for vert in face.verts:
vertuse[vert] += 1
delverts = list()
for vert in self.mesh.verts:
if not vertuse[vert] and vert.index != 0:
delverts.append(vert)
self.mesh.verts.delete(delverts)
#######################################################
## End Remove Doubles Replacement ##
#######################################################
def blender_import_my_faces(self):
@ -848,10 +1124,12 @@ class InterNode(Node):
#Finally, go through, remove dummy vertex, remove doubles and add edgesplit modifier.
Blender.Mesh.Mode(Blender.Mesh.SelectModes['VERTEX'])
self.mesh.verts.delete(0) # remove the dummy vert
self.mesh.sel= 1
self.header.scene.update(1) #slow!
self.mesh.remDoubles(0.0001)
#self.mesh.remDoubles(0.0001)
weldmesh = self.buildWeldMesh()
self.weldFuseFaces(weldmesh)
self.mesh.verts.delete(0) # remove the dummy vert
edgeHash = dict()