Moved ngon creating function from obj_import into BPyMesh so other importers can use it.

Exporter free's mesh vertex data each run (was causing memory errors when exporting fluidsim meshes... poke ken for Mesh.Unlink() since many meshes are still created)
Exporter had an indentation error also.
Added an NMesh wrapper around Mesh for importers to use, so as to work around slow 1 by 1 adding of data. used in upcoming flt_importer update.
Mesh cleanup now has the dangerous option to perform a cleanup on ALL mesh data. (needed it for a project)
This commit is contained in:
Campbell Barton 2006-04-10 21:42:18 +00:00
parent 4043934d2c
commit 9c322c7e86
4 changed files with 273 additions and 109 deletions

@ -32,11 +32,16 @@ def dict2MeshWeight(me, groupNames, vWeightDict):
# Clear the vert group.
currentGroupNames= me.getVertGroupNames()
for group in currentGroupNames:
me.removeVertGroup(group)
if group not in groupNames:
me.removeVertGroup(group) # messes up the active group.
else:
me.removeVertsFromGroup(group)
# Add clean unused vert groupNames back
currentGroupNames= me.getVertGroupNames()
for group in groupNames:
me.addVertGroup(group)
if group not in currentGroupNames:
me.addVertGroup(group)
add_ = Blender.Mesh.AssignModes.ADD
@ -45,7 +50,7 @@ def dict2MeshWeight(me, groupNames, vWeightDict):
vertList[0]= i
for group, weight in vWeightDict[i].iteritems():
try:
me.assignVertsToGroup(group, vertList, weight, add_)
me.assignVertsToGroup(group, vertList, min(1, max(0, weight)), add_)
except:
pass # vert group is not used anymore.
@ -125,4 +130,236 @@ def getMeshFromObject(ob, container_mesh=None, apply_modifiers=True, vgroups=Tru
except:
print 'Cant assign materials to', type
return mesh
return mesh
type_tuple= type( (0,) )
type_list= type( [] )
def ngon(from_data, indices):
'''
takes a polyline of indices (fgon)
and returns a list of face indicie lists.
Designed to be used for importers that need indices for an fgon to create from existing verts.
from_data is either a mesh, or a list/tuple of vectors.
'''
Mesh= Blender.Mesh
Window= Blender.Window
Scene= Blender.Scene
Object= Blender.Object
if len(indices) < 4:
return [indices]
temp_mesh_name= '~NGON_TEMP~'
is_editmode= Window.EditMode()
if is_editmode:
Window.EditMode(0)
try:
temp_mesh = Mesh.Get(temp_mesh_name)
if temp_mesh.users!=0:
temp_mesh = Mesh.New(temp_mesh_name)
except:
temp_mesh = Mesh.New(temp_mesh_name)
if type(from_data) in (type_tuple, type_list):
# From a list/tuple of vectors
temp_mesh.verts.extend( [from_data[i] for i in indices] )
temp_mesh.edges.extend( [(temp_mesh.verts[i], temp_mesh.verts[i-1]) for i in xrange(len(temp_mesh.verts))] )
else:
# From a mesh
temp_mesh.verts.extend( [from_data.verts[i].co for i in indices] )
temp_mesh.edges.extend( [(temp_mesh.verts[i], temp_mesh.verts[i-1]) for i in xrange(len(temp_mesh.verts))] )
oldmode = Mesh.Mode()
Mesh.Mode(Mesh.SelectModes['VERTEX'])
for v in temp_mesh.verts:
v.sel= 1
# Must link to scene
scn= Scene.GetCurrent()
temp_ob= Object.New('Mesh')
temp_ob.link(temp_mesh)
scn.link(temp_ob)
temp_mesh.fill()
scn.unlink(temp_ob)
Mesh.Mode(oldmode)
new_indices= [ [v.index for v in f.v] for f in temp_mesh.faces ]
if not new_indices: # JUST DO A FAN, Cant Scanfill
print 'Warning Cannot scanfill!- Fallback on a triangle fan.'
new_indices = [ [indices[0], indices[i-1], indices[i]] for i in xrange(2, len(indices)) ]
else:
# Use real scanfill.
# See if its flipped the wrong way.
flip= None
for fi in new_indices:
if flip != None:
break
for i, vi in enumerate(fi):
if vi==0 and fi[i-1]==1:
flip= False
break
elif vi==1 and fi[i-1]==0:
flip= True
break
if not flip:
for fi in new_indices:
fi.reverse()
if is_editmode:
Window.EditMode(1)
# Save some memory and forget about the verts.
# since we cant unlink the mesh.
temp_mesh.verts= None
return new_indices
# EG
'''
scn= Scene.GetCurrent()
me = scn.getActiveObject().getData(mesh=1)
ind= [v.index for v in me.verts if v.sel] # Get indices
indices = ngon(me, ind) # fill the ngon.
# Extand the faces to show what the scanfill looked like.
print len(indices)
me.faces.extend([[me.verts[ii] for ii in i] for i in indices])
'''
# NMesh wrapper
Vector= Blender.Mathutils.Vector
class NMesh(object):
__slots__= 'verts', 'faces', 'edges', 'faceUV', 'materials', 'realmesh'
def __init__(self, mesh):
'''
This is an NMesh wrapper that
mesh is an Mesh as returned by Blender.Mesh.New()
This class wraps NMesh like access into Mesh
Running NMesh.update() - with this wrapper,
Will update the realmesh.
'''
self.verts= []
self.faces= []
self.edges= []
self.faceUV= False
self.materials= []
self.realmesh= mesh
def addFace(self, nmf):
self.faces.append(nmf)
def Face(self, v=[]):
return NMFace(v)
def Vert(self, x,y,z):
return NMVert(x,y,z)
def hasFaceUV(self, flag):
if flag:
self.faceUV= True
else:
self.faceUV= False
def addMaterial(self, mat):
self.materials.append(mat)
def update(self, recalc_normals=False): # recalc_normals is dummy
mesh= self.realmesh
mesh.verts= None # Clears the
# Add in any verts from faces we may have not added.
for nmf in self.faces:
for nmv in nmf.v:
if nmv.index==-1:
nmv.index= len(self.verts)
self.verts.append(nmv)
mesh.verts.extend([nmv.co for nmv in self.verts])
for i, nmv in enumerate(self.verts):
nmv.index= i
mv= mesh.verts[i]
mv.sel= nmv.sel
good_faces= [nmf for nmf in self.faces if len(nmf.v) in (3,4)]
#print len(good_faces), 'AAA'
#mesh.faces.extend([nmf.v for nmf in self.faces])
mesh.faces.extend([[mesh.verts[nmv.index] for nmv in nmf.v] for nmf in good_faces])
if len(mesh.faces):
if self.faceUV:
mesh.faceUV= 1
#for i, nmf in enumerate(self.faces):
for i, nmf in enumerate(good_faces):
mf= mesh.faces[i]
if self.faceUV:
if len(nmf.uv) == len(mf.v):
mf.uv= [Vector(uv[0], uv[1]) for uv in nmf.uv]
if len(nmf.col) == len(mf.v):
for c, i in enumerate(mf.col):
c.r, c.g, c.b= nmf.col[i].r, nmf.col[i].g, nmf.col[i].b
if nmf.image:
mf.image= nmf.image
mesh.materials= self.materials[:16]
class NMVert(object):
__slots__= 'co', 'index', 'no', 'sel', 'uvco'
def __init__(self, x,y,z):
self.co= Vector(x,y,z)
self.index= None # set on appending.
self.no= Vector(0,0,1) # dummy
self.sel= 0
self.uvco= None
class NMFace(object):
__slots__= 'col', 'flag', 'hide', 'image', 'mat', 'materialIndex', 'mode', 'normal',\
'sel', 'smooth', 'transp', 'uv', 'v'
def __init__(self, v=[]):
self.col= []
self.flag= 0
self.hide= 0
self.image= None
self.mat= 0 # materialIndex needs support too.
self.mode= 0
self.normal= Vector(0,0,1)
self.uv= []
self.sel= 0
self.smooth= 0
self.transp= 0
self.uv= []
self.v= [] # a list of nmverts.
class NMCol(object):
__slots__ = 'r', 'g', 'b', 'a'
def __init__(self):
self.r= 255
self.g= 255
self.b= 255
self.a= 255

@ -179,13 +179,12 @@ def main():
if is_editmode and (not actob.sel):
obsel.append(actob)
meshes= [ob.getData(mesh=1) for ob in obsel if ob.getType() == 'Mesh']
#====================================#
# Popup menu to select the functions #
#====================================#
CLEAN_ALL_DATA= Draw.Create(0)
CLEAN_VERTS_FREE= Draw.Create(1)
CLEAN_EDGE_NOFACE= Draw.Create(0)
CLEAN_EDGE_SMALL= Draw.Create(0)
@ -213,13 +212,13 @@ def main():
('Weight Normalize', CLEAN_WEIGHT_NORMALIZE, 'Make the sum total of vertex weights accross vgroups 1.0 for each vertex.'),\
'',\
('limit: ', limit, 0.001, 1.0, 'Limit used for the area and length tests above (a higher limit will remove more data).'),\
'',\
('All Mesh Data', CLEAN_ALL_DATA, 'Warning! Operate on ALL mesh objects in your Blend file. Use with care'),\
]
if not Draw.PupBlock('Clean Selected Meshes...', pup_block):
return
CLEAN_VERTS_FREE= CLEAN_VERTS_FREE.val
CLEAN_EDGE_NOFACE= CLEAN_EDGE_NOFACE.val
CLEAN_EDGE_SMALL= CLEAN_EDGE_SMALL.val
@ -230,9 +229,19 @@ def main():
CLEAN_VWEIGHT= CLEAN_VWEIGHT.val
CLEAN_WEIGHT_NORMALIZE= CLEAN_WEIGHT_NORMALIZE.val
limit= limit.val
CLEAN_ALL_DATA= CLEAN_ALL_DATA.val
if is_editmode: Window.EditMode(0)
if CLEAN_ALL_DATA:
if CLEAN_GROUP or CLEAN_VWEIGHT or CLEAN_WEIGHT_NORMALIZE:
# For groups we need the objects linked to the mesh
meshes= [ob.getData(mesh=1) for ob in Object.Get() if ob.getType() == 'Mesh']
else:
meshes= Mesh.Get()
else:
meshes= [ob.getData(mesh=1) for ob in obsel if ob.getType() == 'Mesh']
rem_face_count= rem_edge_count= rem_vert_count= rem_material_count= rem_group_count= rem_vweight_count= 0
for me in meshes:

@ -167,12 +167,12 @@ def copy_images(dest_dir):
# Get MTex images
if matname != None:
mat= Material.Get(matname)
for mtex in mat.getTextures():
if mtex and mtex.tex.type == Blender.Texture.Types.IMAGE:
try:
uniqueImages[mtex.tex.image.name] = None
except:
pass
for mtex in mat.getTextures():
if mtex and mtex.tex.type == Blender.Texture.Types.IMAGE:
try:
uniqueImages[mtex.tex.image.name] = None
except:
pass
# Now copy images
copyCount = 0
@ -463,6 +463,7 @@ EXPORT_GROUP_BY_OB=False, EXPORT_GROUP_BY_MAT=False):
# Make the indicies global rather then per mesh
totverts += len(m.verts)
m.verts= None
file.close()

@ -73,91 +73,7 @@ def stripExt(name): # name is a string
from Blender import *
import BPyImage
reload(BPyImage)
# takes a polyline of indicies (fgon)
# and returns a list of face indicie lists.
def ngon(from_mesh, indicies):
if len(indicies) < 4:
return [indicies]
temp_mesh_name= '~NGON_TEMP~'
is_editmode= Window.EditMode()
if is_editmode:
Window.EditMode(0)
try:
temp_mesh = Mesh.Get(temp_mesh_name)
if temp_mesh.users!=0:
temp_mesh = Mesh.New(temp_mesh_name)
except:
temp_mesh = Mesh.New(temp_mesh_name)
temp_mesh.verts.extend( [from_mesh.verts[i].co for i in indicies] )
temp_mesh.edges.extend( [(temp_mesh.verts[i], temp_mesh.verts[i-1]) for i in xrange(len(temp_mesh.verts))] )
oldmode = Mesh.Mode()
Mesh.Mode(Mesh.SelectModes['VERTEX'])
for v in temp_mesh.verts:
v.sel= 1
# Must link to scene
scn= Scene.GetCurrent()
temp_ob= Object.New('Mesh')
temp_ob.link(temp_mesh)
scn.link(temp_ob)
temp_mesh.fill()
scn.unlink(temp_ob)
Mesh.Mode(oldmode)
new_indicies= [ [v.index for v in f.v] for f in temp_mesh.faces ]
if not new_indicies: # JUST DO A FAN, Cant Scanfill
print 'Warning Cannot scanfill!- Fallback on a triangle fan.'
new_indicies = [ [indicies[0], indicies[i-1], indicies[i]] for i in xrange(2, len(indicies)) ]
else:
# Use real scanfill.
# See if its flipped the wrong way.
flip= None
for fi in new_indicies:
if flip != None:
break
for i, vi in enumerate(fi):
if vi==0 and fi[i-1]==1:
flip= False
break
elif vi==1 and fi[i-1]==0:
flip= True
break
if not flip:
for fi in new_indicies:
fi.reverse()
if is_editmode:
Window.EditMode(1)
# Save some memory and forget about the verts.
# since we cant unlink the mesh.
temp_mesh.verts= None
return new_indicies
# EG
'''
scn= Scene.GetCurrent()
me = scn.getActiveObject().getData(mesh=1)
ind= [v.index for v in me.verts if v.sel] # Get indicies
indicies = ngon(me, ind) # fill the ngon.
# Extand the faces to show what the scanfill looked like.
print len(indicies)
me.faces.extend([[me.verts[ii] for ii in i] for i in indicies])
'''
import BPyMesh
try:
import os
@ -416,12 +332,12 @@ def load_obj(file, IMPORT_MTL=1, IMPORT_EDGES=1, IMPORT_SMOOTH_ALL=0, IMPORT_FGO
materialDict[k]= Material.New(k)
# Make a list of all unused vert indicies that we can copy from
# Make a list of all unused vert indices that we can copy from
VERT_USED_LIST= [-1]*len_vertList
# Here we store a boolean list of which verts are used or not
# no we know weather to add them to the current mesh
# This is an issue with global vertex indicies being translated to per mesh indicies
# This is an issue with global vertex indices being translated to per mesh indices
# like blenders, we start with a dummy just like the vert.
# -1 means unused, any other value refers to the local mesh index of the vert.
@ -435,7 +351,7 @@ def load_obj(file, IMPORT_MTL=1, IMPORT_EDGES=1, IMPORT_SMOOTH_ALL=0, IMPORT_FGO
#meshDict= {} # The 3 variables below are stored in a tuple within this dict for each mesh
currentMesh= NMesh.GetRaw() # The NMesh representation of the OBJ group/Object
#currentUsedVertList= {} # A Dict of smooth groups, each smooth group has a list of used verts and they are generated on demand so as to save memory.
currentMaterialMeshMapping= {} # Used to store material indicies so we dont have to search the mesh for materials every time.
currentMaterialMeshMapping= {} # Used to store material indices so we dont have to search the mesh for materials every time.
# Every mesh has a null smooth group, this is used if there are no smooth groups in the OBJ file.
# and when for faces where no smooth group is used.
@ -456,7 +372,7 @@ def load_obj(file, IMPORT_MTL=1, IMPORT_EDGES=1, IMPORT_SMOOTH_ALL=0, IMPORT_FGO
badObjFaceTexCo= 0
#currentMesh.verts.append(vertList[0]) # So we can sync with OBJ indicies where 1 is the first item.
#currentMesh.verts.append(vertList[0]) # So we can sync with OBJ indices where 1 is the first item.
if len_uvMapList > 1:
currentMesh.hasFaceUV(1) # Turn UV's on if we have ANY texture coords in this obj file.
@ -591,7 +507,7 @@ def load_obj(file, IMPORT_MTL=1, IMPORT_EDGES=1, IMPORT_SMOOTH_ALL=0, IMPORT_FGO
# Vert Index - OBJ supports negative index assignment (like python)
index= int(objVert[0])-1
# Account for negative indicies.
# Account for negative indices.
if index < 0:
if IMPORT_RELATIVE_VERTS: # non standard
index= VERT_COUNT+index+1
@ -713,16 +629,17 @@ def load_obj(file, IMPORT_MTL=1, IMPORT_EDGES=1, IMPORT_SMOOTH_ALL=0, IMPORT_FGO
currentMesh.faces.append(f) # move the face onto the mesh
elif face_vert_count > 4: # NGons.
# we need to map indicies to uv coords.
# we need to map indices to uv coords.
currentMeshRelativeIdxs= [currentUsedVertListSmoothGroup[i] for i in vIdxLs]
if fHasUV:
vert2UvMapping=dict( [ (currentMeshRelativeIdxs[i],vtIdxLs[i]) for i in xrange(face_vert_count)] )
ngon_face_indicies= ngon(currentMesh, currentMeshRelativeIdxs)
ngon_face_indices= BPyMesh.ngon(currentMesh, currentMeshRelativeIdxs)
# At the moment scanfill always makes tri's but dont count on it
for fillFace in ngon_face_indicies:
for fillFace in ngon_face_indices:
f= NMesh.Face([currentMesh.verts[currentMeshRelativeIdxs[i]] for i in fillFace])
if fHasUV:
@ -739,7 +656,7 @@ def load_obj(file, IMPORT_MTL=1, IMPORT_EDGES=1, IMPORT_SMOOTH_ALL=0, IMPORT_FGO
# Set fgon flag.
if IMPORT_FGON:
edgeUsers={}
for fillFace in ngon_face_indicies:
for fillFace in ngon_face_indices:
for i in xrange(len(fillFace)): # Should always be 3
i1= currentMeshRelativeIdxs[fillFace[i]]
i2= currentMeshRelativeIdxs[fillFace[i-1]]
@ -908,7 +825,7 @@ def load_obj_ui(file):
('Create FGons', IMPORT_FGON, 'Import faces with more then 4 verts as fgons.'),\
('Smooth Groups', IMPORT_SMOOTH_GROUPS, 'Only Share verts within smooth groups. (Warning, Hogs Memory)'),\
('Split by Material', IMPORT_MTL_SPLIT, 'Import each material into a seperate mesh (Avoids >16 meterials per mesh problem)'),\
('Relative Verts', IMPORT_RELATIVE_VERTS, 'Import non standard OBJs with relative vertex indicies, try if your mesh imports with scrambled faces.'),\
('Relative Verts', IMPORT_RELATIVE_VERTS, 'Import non standard OBJs with relative vertex indices, try if your mesh imports with scrambled faces.'),\
]
if not os: