import poser import Numeric,os,gzip import tkMessageBox scene = poser.Scene() FLT_EPSILON = 1.19209290e-07 def run(actS,actT,dataPath): if os.path.exists(dataPath): windex,weights = read_file(dataPath,actT.Geometry().NumVertices(),actS.Geometry().NumVertices()) if windex == None: readerror("Datafile for selected actors was not found." "Meshes must be analysed before morphs can be copied.\n") return else: readerror("No data file found" "Meshes must be analysed before morphs can be copied.\n") return flipmap = flip_weights(actS,windex,weights) meshS = myMesh(actS.Geometry(),actS) meshT = myMesh(actT.Geometry(),actT) meshS.init_UV() meshT.init_UV() find_source_splituv(meshS,meshT,flipmap) # Spli texverts transfer_UVs(meshS,meshT,windex,weights) # Transfer UVs # Need to add smoothing routine for split texverts here. map_object(actT.Geometry(),meshT.uvs,meshT.tsets,replace=0) def readerror(message): #app.status_update(message) err = tkMessageBox.showinfo("Error reading datafile",message) def find_source_splituv(meshS,meshT,flipmap): """ Locate best UV seam candiates on target mesh, using flipmap. Use these to build new texverts and texsets listings which weld and split texverts as needed. """ meshT.splits = [[] for i in meshT.verts] meshS.screen = [0 for i in range(meshS.geom.NumVertices())] for vi in range(meshS.geom.NumVertices()): if len(meshS.vuv[vi]) > 1: #testing_boxes(meshS.verts[vi],0.001,"SPLIT",0,1) weights = [] for v in flipmap[vi]: weights.append(v[1]) if weights: w = max(weights) v = flipmap[vi][weights.index(w)][0] #testing_boxes(meshT.verts[v],0.001,"BEST",0,1) #meshT.splits[v].append(meshS.vuv[vi]) meshT.splits[v].append(vi) meshS.screen[vi] = 1 meshT.uvs = [[0.0,0.0] for i in range(meshT.geom.NumVertices())] # Start with welded texverts meshT.tsets = [] screen = [1 for i in meshT.uvs] meshT.puv = [[j for j in i] for i in meshT.pverts] # meshT.pverts is built from Polygon Sets list. Applies texvert welding. for pi in range(meshT.geom.NumPolygons()): p = meshT.pverts[pi] for i in range(len(p)): v = p[i] if meshT.splits[v]: if screen[v]: meshT.uvs.append([0.0,0.0]) meshT.puv[pi][i] = len(meshT.uvs)-1 # Alter TexSets as pverts for more controlled indexing - creating new meshT.puv array screen[v] = 0 meshT.splits.append([[i for i in meshT.splits[v]]]) # Add new splits listing to end of list #testing_boxes(meshT.verts[v],0.0005,"ARE_SPLIT",0,1) for pi in range(meshT.geom.NumPolygons()): # Convert the new meshT.puv to new TexSets array. p = meshT.puv[pi] for i in range(len(p)): meshT.tsets.append(p[i]) meshT.vertuvs(meshT.geom,meshT.pverts,meshT.puv) # Calculate new texverts of verts using new meshT.puv created above meshT.puv = Numeric.array(meshT.puv,Numeric.Int) def transfer_UVs(meshS,meshT,windex,weights): """ Transfer the UVs from the source to the target Does not currently handle seams (vertices with multiple corresponding texvertices) in either the source or the target. """ srcUVs = meshS.uvs points = [i for i in meshT.uvs] for vi in range(meshT.geom.NumVertices()): # Target verts if windex[vi]: for uvi in meshT.vuv[vi]: # Loop through texverts for target vert #print uvi,len(points) point = [0.0,0.0] for w in range(len(windex[vi])): #Get corresponding source texverts win = windex[vi][w] way = weights[vi][w] #for t in range(len(meshS.vuv[win])): #There may be more than 1 texvert per source vert tuvs = meshS.vuv[win][0]#[t] tu = srcUVs[tuvs][0]*way tv = srcUVs[tuvs][1]*way point = [point[0]+tu,point[1]+tv] points[uvi] = [point[0],point[1]] meshT.uvs = [[j for j in i] for i in points] class myMesh(object): def __init__(self,geom,act): self.act = act self.geom = geom self.verts = vertexPos(geom,worldspace=0) def init_UV(self): self.polyverts(self.geom,self.geom.Polygons()) self.polyverts(self.geom,self.geom.TexPolygons(),UV=1) self.vertuvs(self.geom,self.pverts,self.puv) self.UVs() def polyverts(self,geom,polys,UV=0): """ vertices by polygon - this is the Sets() list broken down by polygon index. Returns a list of Numeric arrays """ if UV: self.puv = [] gset = geom.TexSets() else: self.pverts = [] gset = geom.Sets() for p in polys: if UV: l = p.NumTexVertices() else: l = p.NumVertices() s = p.Start() e = s + l vl = Numeric.array([v for v in gset[s:e]],Numeric.Int) if UV: self.puv.append(vl) else: self.pverts.append(vl) def vertuvs(self,geom,pgons,uvgons): """ Returns a Numeric array of vert indices to texvert indices for the geom pgons is pverts or tverts uvgons is puv or tuv Do not mix use of tris and polys! """ self.vuv = [[] for i in range(geom.NumVertices())] screen = [[] for i in range(geom.NumVertices())] for i in range(len(pgons)): for j in range(len(pgons[i])): if not uvgons[i][j] in screen[pgons[i][j]]: self.vuv[pgons[i][j]].append(uvgons[i][j]) screen[pgons[i][j]].append(uvgons[i][j]) def UVs(self): """ Organize the UV data into a Numeric array """ geom = self.geom uvverts = [i for i in geom.TexVertices()] self.uvs = [[] for i in range(geom.NumTexVertices())] for i in range(len(uvverts)): tv = uvverts[i] self.uvs[i] = [tv.U(),tv.V()] self.uvs = Numeric.array(self.uvs,Numeric.Float) uvverts = [] def vertexPos(geom,worldspace=1,multiple=1.0): """ Pre-fetch vertex coordinates for faster access. Returns a Numeric array of vert coords by vert indices Send worldspace=0 keyword when calling function, to use localspace """ numVerts = geom.NumVertices() verts = [[0.0,0.0,0.0] for i in range(numVerts)] verts = Numeric.array(verts,Numeric.Float) for i in range(numVerts): if worldspace: v = geom.WorldVertex(i) else: v = geom.Vertex(i) verts[i] = [v.X(),v.Y(),v.Z()] return verts #=============================================================================================== # build_box() - testing boxes AND region screening boundaries #=============================================================================================== def build_box(b1,name,display,merge,vertarray=None,draw=1): """ Create a box using the Poser geometry API b1 format is [minX,maxX,minY,maxY,minZ,maxZ] if merge == 1, prop will be merged with any pre-existing prop of the same name. """ addmesh = 0 if merge: props = [a for a in scene.Actors() if a.Name().find(name) != -1 and a.IsProp()] if len(props) >= 1: addmesh = 1 if addmesh: newbox = props[0] bbox = newbox.Geometry() else: bbox = poser.NewGeometry() if vertarray == None: Verts = Numeric.array([ [b1[0],b1[2],b1[4]], [b1[0],b1[3],b1[4]], [b1[0],b1[3],b1[5]], [b1[0],b1[2],b1[5]],[b1[1],b1[2],b1[4]],[b1[1],b1[3],b1[4]], [b1[1],b1[3],b1[5]],[b1[1],b1[2],b1[5]] ], Numeric.Float) else: # Use vertarray Verts = vertarray Polys = Numeric.array([[0,4],[4,4],[8,4],[12,4],[16,4],[20,4]],Numeric.Int) Sets = Numeric.array([3,2,1,0,4,5,6,7,5,4,0,1,6,5,1,2,7,6,2,3,4,7,3,0],Numeric.Int) bbox.AddGeneralMesh(Polys,Sets,Verts) if addmesh: newbox.SetGeometry(bbox) newbox.MarkGeomChanged() else: newbox = scene.CreatePropFromGeom(bbox,name) if display == 1: newbox.SetDisplayStyle(poser.kDisplayCodeEDGESONLY) if display == 2: newbox.SetDisplayStyle(poser.kDisplayCodeWIREFRAME) if draw: scene.DrawAll() def testing_boxes(point,sz,name,disp,merge,draw=0): """ build the testing boxes """ b = [point[0]-sz, point[0]+sz, point[1]-sz, point[1]+sz, point[2]-sz, point[2]+sz] build_box(b,name,disp,merge) #=============================================================================================== # File-reading and morph creation (adapted from TDMT Classic with minimal necessary changes) - Original function by Spanki #=============================================================================================== def read_file(path,numverts,srcverts): if not os.path.exists(path): return None, None indices = [[] for i in range(numverts)] weights = [[] for i in range(numverts)] ext = os.path.splitext(path)[1] if ext == ".vmf": thefile = open(path,"r") else: thefile = gzip.GzipFile(path) if thefile: lines = thefile.readlines() thefile.close() header_found = 0 for line in lines: c = line.replace('\n','').split(None) # Split line on white-space #----- First check for blank and/or comment lines if len(c) == 0 or c[0][0] == '#': continue #----- Check for header record if c[0] == 'TDMT_Matched': header_found = 1 if len(c) > 1: version = c[1] # If the script relies on some specific format of the file, it can do that test here if len(c) > 2: sv_count = int(c[2]) if len(c) > 3: dv_count = int(c[3]) if sv_count != srcverts or dv_count != numverts: readerror("Incompatible vertex counts!") return None,None # Maybe insert code to display ok/cancel dialog if vert counts don't match #----- Check for description/comment records elif c[0] == 'comment:': if header_found == 0: readerror("Error reading comment in\n%s" %(line)) return None,None if len(c) > 1: uc = line.split(': ') user_comment = uc[1] #----- Check for actors record elif c[0] == 'actors:': if header_found == 0: readerror("Error reading actors in\n%s" %(line)) return None,None if len(c) > 1: source_actor = c[1] if len(c) > 2: target_actor = c[2] # Do whatever is needed with actor names here (or store for later) elif c[0] == "w": # This section has been altered to allow reading of .vmf files vi = int(c[1]) numweights = int(c[2]) for i in range(numweights): indices[vi].append(int(c[i+3])) if c[0] == "w": for i in range(numweights): weights[vi].append(float(c[i+numweights+3])) return indices, weights else: return None, None #=============================================================================================== # flip_weights() # # This function flips the association of weighting data from being indexed by target mesh indices # to being indexed by source mesh indices. This removes the need for nested looping and scanning # when creating morphs. # (Function drawn from original TDMT code. Written by Spanki.) #=============================================================================================== def flip_weights(actSrc, windex, weights): """Unchanged from TDMT Classic. Written by Spanki.""" numSrcVerts = actSrc.Geometry().NumVertices() numDstVerts = len(windex) flipmap = [[] for i in range(numSrcVerts)] # This table holds lists of index/weight pairs for i in range(numDstVerts): for j in range(len(windex[i])): sndx = windex[i][j] if sndx != -1 and sndx < numSrcVerts: # Sanity check index value weight = weights[i][j] if abs(weight) > FLT_EPSILON: # Don't bother tracking 0.0 weights flipmap[sndx].append([i,weight]) return flipmap def map_object(geom,tverts,tsets,replace=0): """ Apply new UV mapping to the specified actor and create a new prop with the altered geometry. If replace == 1, selected actor will have geometry replaced instead of creating a new prop. """ sets = [i for i in geom.Sets()] polys = [[p.Start(),p.NumVertices()] for p in geom.Polygons()] verts = [[i.X(),i.Y(),i.Z()] for i in geom.Vertices()] sets = Numeric.array(sets,Numeric.Int) polys = Numeric.array(polys,Numeric.Int) verts = Numeric.array(verts,Numeric.Float) #tverts = [[i.U(),i.V()] for i in geom.TexVertices()] #tsets = [i for i in geom.TexSets()] #tsets = [i for i in geom.Sets()] tpolys = [[p.Start(),p.NumVertices()] for p in geom.Polygons()] tverts = Numeric.array(tverts,Numeric.Float) tsets = Numeric.array(tsets,Numeric.Int) tpolys = Numeric.array(tpolys,Numeric.Int) newgeom = poser.NewGeometry() newgeom.AddGeneralMesh(polys,sets,verts,tpolys,tsets,tverts) for mat in geom.Materials(): newgeom.AddMaterialName(mat.Name()) correct_indices = 0 if geom.Materials()[0].Name() != "Preview": correct_indices = 1 for pi in range(geom.NumPolygons()): # Need to find matched polys by incenter poly = geom.Polygon(pi) newpoly = newgeom.Polygon(pi) newpoly.SetMaterialIndex(poly.MaterialIndex()+correct_indices) if replace: scene.CurrentActor().SetGeometry(newgeom) scene.CurrentActor().MarkGeomChanged() else: newprop = scene.CreatePropFromGeom(newgeom,"newprop") scene.SelectActor(newprop) scene.DrawAll() # To let the new material names sink in. for mat in geom.Materials(): matname = mat.Name() diffuse = mat.DiffuseColor() newprop.Material(matname).SetDiffuseColor(diffuse[0],diffuse[1],diffuse[2]) if mat.TextureMapFileName() != None: if os.path.exists(mat.TextureMapFileName()) and not os.path.isdir(mat.TextureMapFileName()): newprop.Material(matname).SetTextureMapFileName(mat.TextureMapFileName()) else: newprop.Material(matname).SetTextureMapFileName(None) dataPath = "C:\\Program Files\\e frontier\\Poser 7\\Runtime\\Python\\poserScripts\\TDMTfiles\\matched\\blMilWom_v3-Antonia-121\\head-head" dataPath = dataPath.replace("\\",os.sep) dataPath = os.path.normpath(os.path.join(dataPath,"posted.vmz")) actS = scene.Figure("Figure 2").Actor("head") actT = scene.Figure("Antonia-121").Actor("head") #actT = scene.Actor("newprop 1") run(actS,actT,dataPath)