import poser import Numeric, time, os, gzip from Tkinter import * no_pyd = 0 # With this transfer script, we only use the .pyd minimally; there is an option to run without it. try: from _tdmt import * except: no_pyd = 1 if (poser.Version() < 7.0) and (not poser.IsPro()): no_pyd = 1 if os.name == 'mac': no_pyd = 1 scene = poser.Scene() root = Tk() do_print = 0 FILE_VERSION = "1.0" compressFiles = 0 FLT_EPSILON = 1.19209290e-07 def run(act1,act2,path,threshold,matsT,matsS,num_influences=5,digits=5): """ num_influences is maximum number of target vertices which should be correlated to any source vertex. digits is number of decimal digits at which .vmf weighting data will be clamped **These are not yet implemented. """ """ From MorphFromObj6.py - how to allow user to set delta trim precision. deltaLine=r'd %d '+ ('%%.%df %%.%df %%.%df\n' % (Num,Num,Num)) deltaValues+='\t\t\t\t'+deltaLine % (i,dx,dy,dz) """ app.status_update("Starting comparison. Please wait....") # act1 is Source # act2 is Target mesh1 = myMesh(act1.Geometry(),"vert",act1) mesh1.get_myMesh_lists() mesh2 = myMesh(act2.Geometry(),"vert",act2) mesh2.get_myMesh_lists(ws=0) #mesh1.clampline = r'%%.%df,%%.%df,%%.%df' % (digits,digits,digits) mesh2.lines = ["" for i in range(mesh2.geom.NumVertices())] close_verts_falloff(mesh1,mesh2,threshold,matsT,matsS,num_influences=num_influences,digits=5) missed = mesh2.lines.count("") if missed == mesh2.geom.NumVertices(): return 0 app.numhits = mesh2.geom.NumVertices() - missed comment = "# Saved with TDMT_Match\n" comment += "# Matched: %s\tMissed: %s\n" %(app.numhits,missed) if matsT.count(1) or matsS.count(1): comment += "# Target materials:\n" # For user reference only comment += "#\t%s\n" %(matsT) m = act2.Geometry().Materials() matnames = [m[i].Name() for i in range(len(m)) if matsT[i] == 1] comment += "#\t%s\n" %(matnames) comment += "# Source materials:\n" comment += "#\t%s\n" %(matsS) m = act1.Geometry().Materials() matnames = [m[i].Name() for i in range(len(m)) if matsS[i] == 1] comment += "#\t%s\n" %(matnames) write_matched(act1,act2,mesh2,path,myComment=comment) del mesh1 del mesh2 return 1 def screen_vmats(act,mesh,screen1): """ Materials by vertex - verts can belong to as many materials as polys. Need to count them up and decement them to return a good matscreen list for verts. """ mesh.polyverts(mesh.geom,mesh.geom.Polygons()) mscreen = [-1 for i in range(mesh.geom.NumVertices())] for pi in range(mesh.geom.NumPolygons()): nvs = mesh.pverts[pi] pmi = act.Geometry().Polygon(pi).MaterialIndex() for v in nvs: if screen1[pmi] == 1: mscreen[v] = 1 inc_exc = app.incVar.get() if not inc_exc: # Exclusive = old default inc = 1 exc = 0 setting = 0 else: inc = 0 # Inclusive exc = 1 setting = 1 screen = [inc for i in range(mesh.geom.NumVertices())] for pi in range(mesh.geom.NumPolygons()): p = act.Geometry().Polygon(pi) ngv = mesh.pverts[pi] for vi in ngv: if mscreen[vi] == setting: screen[vi] = exc mesh.screenT = [i for i in screen] 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 #=============================================================================================== # class myMesh() #=============================================================================================== class myMesh(object): """Initialize the mesh data""" def __init__(self,geom,meshtype,act): if meshtype == "vert": if not no_pyd: self.mesh = Mesh(geom) # pyd Mesh instance embedded self.act = act self.geom = geom self.safe_mesh = 1 # Set to zero if mesh has stray vertices if meshtype == "poly": if not no_pyd: self.mesh = Mesh(geom) def get_myMesh_lists(self,verts=1,ws=1): """ A bit of control for instantiation of common use myMesh lists """ if verts: if not no_pyd: self.verts = self.mesh.GetWorldVertices() else: self.verts = vertexPos(self.geom,worldspace=ws) def polyverts(self,geom,polys): """ vertices by polygon - this is the Sets() list broken down by polygon index. Returns a list of Numeric arrays """ self.pverts = [] gset = geom.Sets() for p in polys: l = p.NumVertices() s = p.Start() e = s + l vl = Numeric.array([v for v in gset[s:e]],Numeric.Int) self.pverts.append(vl) #------from blender2cal3d.py------ (with some substitution of Numeric) def point_distance(p1, p2): return Numeric.sqrt((p2[0] - p1[0]) ** 2 + \ (p2[1] - p1[1]) ** 2 + (p2[2] - p1[2]) ** 2) def squared_distance(p1,p2): return (p2[0] - p1[0]) ** 2 + \ (p2[1] - p1[1]) ** 2 + (p2[2] - p1[2]) ** 2 #=============================================================================================== # Correlation methods #=============================================================================================== def close_verts_falloff(mesh1,mesh2,threshold,matsT,matsS,num_influences=5,digits=5): """ Match verts by submitted area with weighted influence by distance. This is the main function of the script. """ import math mesh1.screenT = [1 for i in range(mesh1.geom.NumVertices())] if matsS.count(1): screen_vmats(mesh1.act,mesh1,matsS) mesh2.screenT = [1 for i in range(mesh2.geom.NumVertices())] if matsT.count(1): screen_vmats(mesh2.act,mesh2,matsT) grid_cells = {} for nvi in range(mesh1.geom.NumVertices()): p = mesh1.verts[nvi] key = tuple([int(math.floor(x / threshold)) for x in p]) grid_cells.setdefault(key, []).append(nvi) exact = 0 inexact = 0 numv = mesh2.geom.NumVertices() old_amount = 0 for vi in range(numv): # Print percentage of progress for comparison amount = int((float(vi+1)/float(numv))*100) if vi and (amount%5 == 0) and (amount != old_amount): progress = "Vertex %s - Processed %s%s. Please wait...." %(vi,amount,"%") app.status_update(progress) old_amount = amount if mesh2.screenT[vi]: vert = mesh2.verts[vi] a, b, c = tuple([int(math.floor(x /threshold)) for x in vert]) candidates = [] for x in range(a-1,a+2): for y in range(b-1,b+2): for z in range(c-1,c+2): candidates += (grid_cells.get((x,y,z)) or []) dists = {} go = 0 xmin, xmax = vert[0] - threshold, vert[0] + threshold ymin, ymax = vert[1] - threshold, vert[1] + threshold zmin, zmax = vert[2] - threshold, vert[2] + threshold #for nvi in range(mesh1.geom.NumVertices()): for nvi in candidates: if mesh1.screenT[nvi]: p2 = mesh1.verts[nvi] if (p2[1] < ymin or p2[1] > ymax or p2[0] < xmin or p2[0] > xmax or p2[2] < zmin or p2[2] > zmax): continue if no_pyd: dist = (p2[0]-vert[0])**2 + (p2[1]-vert[1])**2 + (p2[2]-vert[2])**2 #2.48 min else: dist = vert.PointDistance(mesh1.verts[nvi]) #48 seconds """ #dist = point_distance(vert,mesh1.verts[nvi]) #4.91 min #dist = squared_distance(vert,mesh1.verts[nvi]) #2.78 min #p2 = mesh1.verts[nvi] #dist = Numeric.sqrt((p2[0]-vert[0])**2 + (p2[1]-vert[1])**2 + (p2[2]-vert[2])**2) #4.58 min """ if ((not no_pyd) and dist <= threshold) or (no_pyd and dist <= threshold**2): if not go: go = 1 else: continue if dists.has_key(dist): dists[dist].append(nvi) else: dists[dist] = [nvi] if go: sortdists = dists.keys() sortdists.sort() num_influences2 = min(num_influences+1,len(sortdists)) totdist = 0.0 closest = [] for i in range(num_influences2): dist = sortdists[i] closest.append(dist) totdist += dist dmin = min(closest) dmax = max(closest) if not (dmin > FLT_EPSILON): # Darned close to a perfect match matched = dists[dmin] # May be multiple verts at dmin lenm = len(matched) line = "w %s %s" %(vi,lenm) weight = 1.0/lenm for mvi in matched: line += " %s" %(mvi) for mvi in matched: line += " %.5f" %(weight) line += "\n" mesh2.lines[vi] = line exact += 1 continue matched = [dists[i] for i in closest] weights = falloff2(closest,dmax=dmax) lentot = 0 for i in matched: lentot += len(i) indices = "" sweights = "" for ci in range(len(closest)): weight = weights[ci] weight /= len(matched[ci]) if not (weight > FLT_EPSILON): lentot -= len(matched[ci]) continue for di in matched[ci]: indices += " %s" %(di) sweights += " %.5f" %(weight) line = "w %s %s" %(vi,lentot) line += indices line += sweights line += "\n" mesh2.lines[vi] = line inexact += 1 app.status_update("Exact: %s/%s; Inexact: %s/%s" %(exact,numv,inexact,numv)) dists = {} def falloff2(dists,dmin=0.0,dmax=100.0): """Calculate weights for correlated vertices, by distance.""" total = 0.0 result = [0.0 for i in dists] for di in range(len(dists)): dist = dists[di] if dist <= dmin: oneresult = [0.0 for i in dists] oneresult[di] = 1.0 return oneresult val = (dmax - dist) / (dmax - dmin) total += val result[di] = val if total <= FLT_EPSILON: return [1.0/len(dists) for i in dists] for i in range(len(result)): result[i] /= total return result #=============================================================================================== # File-writing (adapted from TDMT Classic with minimal necessary changes) - Original function by Spanki. #=============================================================================================== def write_matched(act1,act2,mesh2,path,myComment=""): """ Write the correlation file. A vertex match file (.wmf or .vmz) is based on the TDMT .vwt file, with changes largely designed to allow an arbitrary number (from 1 to n) of weighted correlations. """ version = 1.0 if compressFiles: fOut = gzip.GzipFile(path,'wb') else: fOut=open(path,'wt') text = "#-------------------------------------------------------------------------------------\n" text += "# Sample comment - Insert some witty text here.\n" text += "#\n" text += "# (this could list copyrights, etc.)\n" text += myComment text += "#-------------------------------------------------------------------------------------\n" fOut.write(text) text = "TDMT_Matched %s %i %i\n\n" %(version, act1.Geometry().NumVertices(), act2.Geometry().NumVertices()) fOut.write(text) user_comment = "sample comment - just ignore" text = "comment: %s\n\n" %(user_comment) fOut.write(text) a1 = act1.InternalName().split(":")[0].replace(" ","_") a2 = act2.InternalName().split(":")[0].replace(" ","_") text = "actors: %s-%s\n\n" %(a1, a2) fOut.write(text) text = "#-------------------------------------------------------------------------------------\n" text += "# vertex match data...\n" text += "# w (vertex index) (number of matches(N)) (matched vertex indices x N) (weights x N)\n" text += "#-------------------------------------------------------------------------------------\n" fOut.write(text) for line in mesh2.lines: fOut.write(line) fOut.close() #=============================================================================================== # Path-handling functions #=============================================================================================== def geomPath(a1,a2,dataName,overwrite=0): """ Get the necessary actor and geometry file information, to construct the dataPath. """ gf1, name1 = get_geomfiles(a1) gf2, name2 = get_geomfiles(a2) dataPath = get_pathname(gf1,gf2,name1,name2,create=1,dataName=dataName) if not dataPath: return "" if not overwrite: dataPath = protect_naming(dataPath) return os.path.normpath(dataPath) def get_geomfiles(act): """ act.GeomFileName() will equal figure.GeomFileName() unless a figure actor uses GeomCustom (GeomFileName() == None) or has an external geometry listing in the.cr2. """ agf = act.GeomFileName() if agf: fig = act.ItsFigure() if fig: fgf = fig.GeomFileName() if fgf: if fgf == agf: return os.path.splitext(os.path.basename(fgf))[0],\ act.InternalName().split(":")[0].replace(" ","_") # Normal figure actor else: return os.path.splitext(os.path.basename(fgf))[0],\ "%s_ext" %(os.path.splitext(os.path.basename(agf))[0]) # Figure actor with external geom listing return os.path.splitext(os.path.basename(agf))[0],\ act.InternalName().split(":")[0].replace(" ","_") # Standard prop with external geom listing if act.IsBodyPart(): fgf = fig.GeomFileName() if fgf: #return os.path.splitext(os.path.basename(fgf))[0],\ #"%s_cust" %(act.InternalName().split(":")[0].replace(" ","_")) # Figure actor with GeomCustom return os.path.splitext(os.path.basename(fgf))[0],\ act.InternalName().split(":")[0].replace(" ","_") # Normal figure actor return os.path.join(os.path.dirname(poser.AppLocation()),"%s.obj" %(act.Name())),\ act.InternalName().split(":")[0].replace(" ","_") # Any other actor with GeomCustom use def get_pathname(g1,g2,a1,a2,create=1,dataName="full"): """ Get the path and file name for datafile, creating folders if needed. Some systems may require administrator access to create the directories. g1,g2 are GeomFileNames; a1,a2 are actor InternalNames. """ corr_folder = "matched" ext = ".vmf" if compressFiles: ext = ".vmz" pyfolder = os.path.join("Runtime","Python") pyfolder2 = os.path.join(pyfolder,"poserScripts") base = os.path.join(os.path.dirname(poser.AppLocation()),pyfolder2) if not os.path.exists(base): return "" scriptfolder = os.path.join(base,"TDMTfiles") if not os.path.exists(scriptfolder): try: os.mkdir(os.path.abspath(scriptfolder)) except: return "" scriptfolder2 = os.path.join(scriptfolder,corr_folder) # Changed to reflect new version number if not os.path.exists(scriptfolder2): try: os.mkdir(os.path.abspath(scriptfolder2)) except: return "" geomfolder = os.path.join(scriptfolder2, "%s-%s" %(g1.replace(" ","_"),g2.replace(" ","_"))) if create: if not os.path.exists(geomfolder): try: os.mkdir(os.path.abspath(geomfolder)) except: return "" actfolder = os.path.join(geomfolder, "%s-%s" %(a1.replace(" ","_"),a2.replace(" ","_"))) if create: if not os.path.exists(actfolder): try: os.mkdir(os.path.abspath(actfolder)) except: return "" dataPath = os.path.abspath(os.path.join(actfolder,"%s%s" %(dataName,ext))) if not create: return dataPath # ? if os.path.exists(actfolder): return dataPath else: return "" def protect_naming(path): folder = os.path.dirname(path) filename = os.path.basename(path) name,ext = filename.split(".") if os.path.exists(path): num = 0 newname = "%s_%s.%s" %(name,num,ext) while os.path.exists(os.path.join(folder,newname)): num += 1 newname = "%s_%s.%s" %(name,num,ext) return os.path.join(folder,newname) else: return path #=============================================================================================== # Morph-related functions #=============================================================================================== def unique_name(morphName,act,suffix,internal=0): """ Keep dial naming for the new morph from being automatically changed by Poser Used by transfer_morph, transfer_shape, inflate_normals, and the smoothing functions. """ #Morph dial names max out at 30 characters, after which Poser #won't recognize the dial as a morph (?!?) if len(morphName) >= 20 and suffix != "": morphName = morphName[0:12] if suffix != "" and morphName.find(suffix) == -1: morphName = "%s%s" %(morphName,suffix) # Check the dial name to avoid duplication if internal: parms = [p.InternalName() for p in act.Parameters()] else: parms = [p.Name() for p in act.Parameters()] if morphName in parms: temp = 1 while "%s_%i" %(morphName,temp) in parms: temp += 1 # Name the target morph dial morphName = "%s_%i" %(morphName,temp) return morphName #=============================================================================================== # Compression-handling #=============================================================================================== def gzip_data(dataPath,replace=0,swap_ext=1): """ Compress or decompress file, with option to replace. """ try: compressed = check_gzip(dataPath) if swap_ext: path,ext = dataPath.split(".") if ext == "vmf": dataPath = "%s.vmz" %(path) if ext == "vmz": dataPath = "%s.vmf" %(path) if compressed: f = gzip.GzipFile(dataPath) else: f = open(dataPath,"r") g = f.read() f.close() if not replace: dataPath = protect_naming(dataPath) if compressed: f = open(dataPath,"w") else: f = gzip.GzipFile(dataPath,"wb") f.write(g) f.close() except: print "gzip_data() error: could not process selected file." return def check_gzip(dataPath): f = open(dataPath,"rb") magic = f.read(2) f.close() if magic == '\037\213': return 1 else: return 0 class App: def __init__(self, master): self.master = master master.title("TDMT Closest Vertices Comparison 1") self.gzVar = IntVar() self.gzVar.set(0) self.incVar = IntVar() self.incVar.set(1) self.figS = None self.actS = None self.basepath = "Runtime:Python:PoserScripts:TDMTfiles:" self.corrfolder = "matched:" self.numhits = 0 self.masterFrame = Frame(self.master) self.masterFrame.grid(row=1,column=0) self.displayFrame = Frame(self.master) self.displayFrame.grid(row=2,column=0) self.ListFrame = Frame(self.masterFrame,borderwidth=2,relief=RIDGE) self.ListFrame.grid(row=1,column=0) self.ListFrame2 = Frame(self.masterFrame,borderwidth=2,relief=RIDGE) self.ListFrame2.grid(row=1,column=1) self.matFrame = Frame(self.masterFrame,borderwidth=2,relief=RIDGE) self.matFrame.grid(row=1,column=2) self.mixFrame = Frame(self.masterFrame,borderwidth=2,relief=RIDGE) self.mixFrame.grid(row=1,column=3) self.normFrame = Frame(self.mixFrame,borderwidth=2,relief=RIDGE) self.normFrame.grid(row=0,column=0) self.checkFrame = Frame(self.mixFrame,borderwidth=2) self.checkFrame.grid(row=1,column=0) self.radioFrame = Frame(self.checkFrame,borderwidth=2,relief=RIDGE) self.radioFrame.grid(row=1,column=0) self.buttonFrame = Frame(self.mixFrame,borderwidth=2) self.buttonFrame.grid(row=2,column=0) self.quitFrame = Frame(self.mixFrame,borderwidth=3) self.quitFrame.grid(row=4,column=0) # --- Listboxes --- self.listLabel = Label(self.ListFrame, text="Target Actor" , anchor=N, justify=LEFT) self.listLabel.grid(row=1, column=1) self.ListScroll = Scrollbar(self.ListFrame, orient=VERTICAL) # Target actor self.ListScroll.grid( row=2, column=0,sticky=N+S+E) self.List = Listbox(self.ListFrame, height=20, width=25, selectmode=SINGLE,exportselection=0, yscrollcommand=self.ListScroll.set) self.List.grid( row=2, column=1) self.ListScroll["command"] = self.List.yview self.listLabel2 = Label(self.ListFrame2, text="Source Actor(s)" , anchor=N, justify=LEFT) self.listLabel2.grid(row=1, column=1) self.ListScroll2 = Scrollbar(self.ListFrame2, orient=VERTICAL) # Source actors self.ListScroll2.grid( row=2, column=0,sticky=N+S+E) self.List2 = Listbox(self.ListFrame2, height=20, width=25, selectmode=SINGLE,exportselection=0, yscrollcommand=self.ListScroll2.set) self.List2.grid( row=2, column=1) self.ListScroll2["command"] = self.List2.yview self.List4 = Listbox(self.displayFrame, height=10, width=106, selectmode=SINGLE,exportselection=0) # Status bar self.List4.grid( row=3, column=0) self.matLabel = Label(self.matFrame, text="Target Materials" , anchor=N, justify=LEFT) self.matLabel.grid(row=1, column=1) self.matScroll = Scrollbar(self.matFrame, orient=VERTICAL) # Target materials self.matScroll.grid( row=2, column=0,sticky=N+S+E) self.matL = Listbox(self.matFrame, height=9, width=25, selectmode=MULTIPLE,exportselection=0, yscrollcommand=self.matScroll.set) self.matL.grid( row=2, column=1) self.matScroll["command"] = self.matL.yview self.matLabel2 = Label(self.matFrame, text="Source Materials" , anchor=N, justify=LEFT) self.matLabel2.grid(row=3, column=1) self.matScroll2 = Scrollbar(self.matFrame, orient=VERTICAL) # Source materials self.matScroll2.grid( row=4, column=0,sticky=N+S+E) self.matL2 = Listbox(self.matFrame, height=9, width=25, selectmode=MULTIPLE,exportselection=0, yscrollcommand=self.matScroll2.set) self.matL2.grid( row=4, column=1) self.matScroll2["command"] = self.matL2.yview # --- Radio Buttons --- self.radioLabel = Label(self.radioFrame, text="Vertex Material Edge\nScreening:",anchor=N,justify=CENTER) self.radioLabel.grid(row=1,column=0) self.radioInj = Radiobutton(self.radioFrame, text="Inclusive", variable=self.incVar, value=1) self.radioInj.grid(row=2, column=0) self.radioRem = Radiobutton(self.radioFrame, text="Exclusive", variable=self.incVar, value=0) self.radioRem.grid(row=3, column=0) # --- Checkbuttons --- self.gzCheck = Checkbutton(self.checkFrame,text="Compress Files", variable=self.gzVar) self.gzCheck.grid(row = 0, column = 0) # --- Entries --- self.distepLabel = Label(self.buttonFrame, text="Distance Cutoff" , anchor=N, justify=LEFT) self.distepLabel.grid(row=4, column=0) self.distepEntry = Entry(self.buttonFrame, width=8) self.distepEntry.insert(0,"0.006") self.distepEntry.grid(row=5,column=0) self.infLabel = Label(self.buttonFrame, text="Number of Influences" , anchor=N, justify=LEFT) self.infLabel.grid(row=6, column=0) self.infEntry = Entry(self.buttonFrame, width=8) self.infEntry.insert(0,"5") self.infEntry.grid(row=7,column=0) self.pathEntry = Entry(self.displayFrame, width=106) self.pathEntry.insert(0,"%s%s" %(self.basepath,self.corrfolder)) self.pathEntry.grid(row=1,column=0) # --- Buttons --- self.buttonRun = Button(self.quitFrame, text="Run", command=self.handleRun) self.buttonRun.grid(row=0, column=0) self.buttonQuit = Button(self.quitFrame, text="Quit", command=self.die) self.buttonQuit.grid(row=0, column=1) # ---- Mouse-button binding ---- self.List.bind('',lambda e, s=self: s.handleTarget(e.y)) self.List2.bind('',lambda e, s=self: s.handleSources(e.y)) self.fill_boxes(self.List) self.pyd_update() self.status_update("Ready") def status_update(self,line): self.List4.insert(0,line) self.List4.update() def path_update(self,path): path = path.replace(os.sep,":") path = "Runtime:%s" %(path.split("Runtime:")[1]) self.pathEntry.delete(0,END) self.pathEntry.insert(0,path) self.pathEntry.index(len(self.basepath)-1) # self.pathEntry.update() def pyd_update(self): if not no_pyd: self.status_update("Running using Spanki's tdmt.pyd for greater speed.") else: self.status_update("Not running using Spanki's tdmt.pyd") if poser.Version() < 7.0: self.status_update("Reason: Poser version is less than 7.0.") if os.name == "mac": self.status_update("Reason: OS is Mac.") if poser.Version() >= 7.0 and os.name != "mac": self.status_update("Reason: tdmt.pyd not found.") def handleTarget(self,event): self.figS = None self.List2.delete(0,END) self.matL.delete(0,END) self.matL2.delete(0,END) clicked = self.List.nearest(event) s1 = self.List.get(clicked) name,fig = s1.split(" <") fig = fig.replace(">","") if fig == "None": self.actS = scene.Actor(name) else: self.figS = scene.Figure(fig) self.actS = self.figS.Actor(name) self.fill_boxes(self.List2,self.actS.InternalName()) mats = self.get_mats(self.actS) for mat in mats: self.matL.insert(END,mat) def handleSources(self,event): clicked = self.List2.nearest(event) s1 = self.List2.get(clicked) name,fig = s1.split(" <") fig = fig.replace(">","") if fig == "None": actS = scene.Actor(name) figS = None else: figS = scene.Figure(fig) actS = figS.Actor(name) mats = self.get_mats(actS) matlist = self.matL2.get(0,END) for mat in mats: if mat not in matlist: self.matL2.insert(END,mat) def get_mats(self,act): """ It should be noted that act.Materials() != act.Geometry().Materials(). The Geometry materials are inherited from the .obj file. The Actor materials are inherited from the .cr2, and may contain unused listings, as well as the ubiquitous and often extraneous Preview material. Because of this mismatch, the two Materials() lists are not compatible and cannot be cross-indexed! This is one of the (many) things which is not clarified in the PPy docs, although it does make sense. Actor.Geometry().Materials() should generally be the same as UnimeshInfo[0].Materials(), although it seems the latter may sometimes contain mysterious duplicate listings. At any rate, the act.Geometry().Materials() will return all materials used in the unimesh for the figure. If an actor has GeomCustom use, however, the act.Geometry().Materials() will reflect only those for that actor and will not necessarily list all (or any) mats found in UnimeshInfo[0].Materials(). This applies to external geometry listings as custom geometry in the .cr2, as well. Cage learned all of this the hard way. :-P """ m = act.Geometry().Materials() matnames = [] for p in act.Geometry().Polygons(): #Display only materials present in the selected actor. mn = p.MaterialName() if not mn in matnames: matnames.append(mn) return ["%s <%s>" %(m[i].Name(),i) for i in range(len(m)) if m[i].Name() in matnames] def fill_boxes(self,theList,notme=""): """ Adds the figures and props to the first set of listboxes """ for act in scene.Actors(): if act.IsLight() or act.IsCamera() or act.IsDeformer() or act.IsBase() or\ act.IsZone() or (not act.Geometry()) or act.Name() == 'GROUND' or\ act.Name() == 'FocusDistanceControl' or 'CenterOfMass' in act.Name(): continue if not act.OnOff(): continue if act.InternalName() == notme: continue line = [act.Name()] if act.ItsFigure(): line.append(act.ItsFigure().Name()) else: line.append(act.ItsFigure()) theList.insert(END,"%s <%s>" %(line[0],line[1])) def die(self): """End the script""" root.destroy() root.quit() def handleRun(self): global compressFiles if not self.actS: # No target actor selected self.status_update("No Target actor selected.") return StartTime = time.time() self.numhits = 0 s = self.List2.curselection() if s != (): t = self.List2.get(s) name,fig = t.split(" <") fig = fig.replace(">","") if fig == "None": actS = scene.Actor(name) else: actS = scene.Figure(fig).Actor(name) else: # No source actor selected self.status_update("No Source actor selected.") return if actS.ItsFigure(): figSn = actS.ItsFigure().Name() else: figSn = "" actT = self.actS if actT.ItsFigure(): figTn = actT.ItsFigure().Name() else: figTn = "" compressFiles = self.gzVar.get() try: distEpsilon = float(self.distepEntry.get()) except ValueError: distEpsilon = 0.006 try: influences = int(self.infEntry.get()) except ValueError: influences = 5 mT = [self.matL.get(int(i)).split(" <") for i in self.matL.curselection()] matsT = [-1 for i in range(len(actT.Materials()))] mS = [self.matL2.get(int(i)).split(" <") for i in self.matL2.curselection()] matsS = [-1 for i in range(len(actS.Materials()))] for m in mT: matname,index = m index = index.replace(">","") matsT[int(index)] = 1 for m in mS: matname,index = m index = index.replace(">","") matsS[int(index)] = 1 dataName = "full" if matsS.count(1) or matsT.count(1): dataName = "part" path = geomPath(actS,actT,dataName,overwrite=0) if path: self.path_update(path) else: self.status_update("Error getting filepath for .vmf files.") return if not os.path.exists(path): self.status_update("Comparing %s %s to %s %s...." %(figSn,actS.Name(),figTn,actT.Name())) goodrun = run(actS,actT,path,distEpsilon,matsT,matsS,num_influences=influences,digits=5) if goodrun: EndTime = (time.time() - StartTime)/60 minutes = int(EndTime) seconds = int((EndTime - float(minutes))*60) self.status_update("Done in %s minutes, %s seconds. Matched %s of %s" %(minutes,seconds,self.numhits,actT.Geometry().NumVertices())) else: self.status_update("No matches found for %s %s to %s %s." %(figSn,actS.Name(),figTn,actT.Name())) app = App(root) root.mainloop()