import poser from Tkinter import * from _tdmt import * scene = poser.Scene() root = Tk() do_print = 0 def run(screen,reps,threshold,morphs,highlow,screenlist): theAct = scene.CurrentActor() theMesh = myMesh(theAct.Geometry()) # Instantiate the mesh theMesh.baseverts = theMesh.mesh.GetVertices() # Used for worldspace removal in setShape theMesh.detailverts = theMesh.mesh.GetVertices() # Used by find_detail; can have morph deltas added in. theMesh.verts = theMesh.mesh.GetWorldVertices() # Used for worldspace removal in setShape theMesh.points = theMesh.mesh.GetWorldVertices() # Store hit locations to pass to post-processing functions theMesh.matchlist = [-1 for i in theMesh.verts] # Screening for restore_detail if morphs: # Mix in deltas for selected morphs mix_deltas(theAct,theMesh,morphs) find_detail(theMesh) restore_detail(theMesh,reps=reps,screen=screen,threshold=threshold,highlow=highlow,screenlist=screenlist) setShape(theMesh,theAct) class myMesh(object): """Initialize the mesh data - just a container class for pyd Mesh instance""" def __init__(self,geom): self.mesh = Mesh(geom) # pyd Mesh instance embedded def mix_deltas(act,mesh,morphs): for name in morphs: mt = act.Parameter(name) for vi in range(mesh.mesh.NumVertices()): d = Vector() + mt.MorphTargetDelta(vi) if d: mesh.detailverts[vi] += d def find_detail(mesh): """ Finds center (averaged position) of all neighbor vertices for each vertex. These are then used by restore_detail to try to reconstruct the basic vertex relationships after the shrink-wrapping. Use of centers integrates a smoothing effect into the process, which both shrinks the mesh and removes detail. """ mesh.vneighbors = mesh.mesh.GetVertNeighborsByNgons() mesh.detail = [Vector() for i in mesh.detailverts] mesh.dists = [0.0 for i in mesh.detailverts] for vi in range(len(mesh.detailverts)): nbs = mesh.vneighbors[vi] vert = mesh.detailverts[vi] avg = Vector() for i in range(len(nbs)): nvi = nbs[i] nv = mesh.detailverts[nvi] for i in range(3): avg[i] += nv[i] for i in range(3): avg[i] /= len(nbs) mesh.detail[vi] = vert - avg # Detail entry is a delta/direction vector mesh.dists[vi] = vert.PointDistance(avg) def restore_detail(mesh,reps=1000,screen=1,threshold=0.0015,highlow=0,screenlist=[1,1,1]): #reps = Maximum number of looped repititions for function #screen = favor verts without matches 0=process all, 1=process only unmatched verts #threshold = difference in distance between base geom and adjusted shape at which script will stop affecting a vert #highlow = screen by whether distance is higher or lower at outset. 0 = low; 1 = high #screenlist = screen x,y, or z component of restoration by sending 0 instead of one for the proper indexed position in the list detail = mesh.detail dists = mesh.dists oldcount = mesh.matchlist.count(1) for rep in range(reps): points = mesh.points for vi in range(len(mesh.detailverts)): if ((screen == 1) and (mesh.matchlist[vi] == -1)) or screen == 0: vert = mesh.detailverts[vi] nbs = mesh.vneighbors[vi] avg = Vector() for ni in range(len(nbs)): nvi = nbs[ni] for i in range(3): avg[i] += points[nvi][i] for i in range(3): avg[i] /= len(nbs) dist = vert.PointDistance(avg) distdiff = dist - dists[vi] if ((not highlow) and distdiff <= threshold) or (highlow and distdiff >= threshold): mesh.matchlist[vi] = 1 newpoint = points[vi].Clone() # Reconstruct mesh shape relative to averaged positions of all vertneighbors (applies smoothing) for i in range(3): if screenlist[i]: newpoint[i] = avg[i] + detail[vi][i] mesh.points[vi] = newpoint.Clone() if do_print: print rep,mesh.matchlist.count(1),oldcount if mesh.matchlist.count(1) == oldcount: # Quit once we reach the point where passes don't change the count - point of diminishing returns break oldcount = mesh.matchlist.count(1) def setShape(mesh,theAct,addDeltas=0): # Set the morphs or bake the shape """create the morphs""" morphs = {} if addDeltas: deltas = [Vector() for i in mesh.verts] for parm in theAct.Parameters(): if parm.IsMorphTarget(): morphs[parm.Name()] = parm.Value() if addDeltas: if parm.Value() <> 0.0: pv = parm.Value() for vi in range(mesh.mesh.NumVertices()): md = Vector() + parm.MorphTargetDelta(vi) # coerce tuple into Vector md = md.Scale(pv) # scale by parameter setting deltas[vi] += md.Clone() # merge it into the deltas list parm.SetValue(0.0) mtname = unique_name("restore_detail",theAct,"") theAct.SpawnTarget(mtname) newMT = theAct.Parameter(mtname) newMT.SetValue(1.0) for parmname in morphs.keys(): parm = theAct.Parameter(parmname) parm.SetValue(morphs[parmname]) for vi in range(len(mesh.points)): vec = ((mesh.points[vi] - mesh.baseverts[vi]) - (mesh.verts[vi] - mesh.baseverts[vi])) # Calculate delta, remove world transforms and deformations if addDeltas: # Optionally add morph settings back in vec += deltas[vi] if vec: newMT.SetMorphTargetDelta(vi,vec.x,vec.y,vec.z) scene.ProcessSomeEvents() scene.DrawAll() #=============================================================================================== # unique_name() #=============================================================================================== def unique_name(morphName,act,suffix,internal=0): """ Keep dial naming for the new morph from being automatically changed by Poser """ #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 class App: def __init__(self, master): self.master = master master.title("Restore Detail") self.scrVar = IntVar() self.scrVar.set(1) self.xVar = IntVar() self.xVar.set(1) self.yVar = IntVar() self.yVar.set(1) self.zVar = IntVar() self.zVar.set(1) self.hiloVar = IntVar() self.hiloVar.set(1) self.masterFrame = Frame(self.master,borderwidth=2,relief=RIDGE) self.masterFrame.grid(row=1,column=0) self.mixFrame = Frame(self.masterFrame,borderwidth=2,relief=RIDGE) self.mixFrame.grid(row=0,column=0) self.checkFrame = Frame(self.mixFrame) self.checkFrame.grid(row=0,column=0) self.buttonFrame = Frame(self.mixFrame) self.buttonFrame.grid(row=1,column=0) self.ListFrame2 = Frame(self.masterFrame,borderwidth=2,relief=RIDGE) self.ListFrame2.grid(row=0,column=1) # --- Radio buttons --- self.radioHi = Radiobutton(self.checkFrame,text="Screen Low",variable=self.hiloVar,value=0) self.radioHi.grid(row=0,column=0) self.radioLo = Radiobutton(self.checkFrame,text="Screen High",variable=self.hiloVar,value=1) self.radioLo.grid(row=1,column=0) # --- Checkbuttons --- self.scrCheck = Checkbutton(self.checkFrame,text="Screen matched\nverts", variable=self.scrVar) self.scrCheck.grid(row = 2, column = 0) self.scr2Check = Checkbutton(self.checkFrame,text="Use X", variable=self.xVar) self.scr2Check.grid(row = 3, column = 0) self.scr2Check = Checkbutton(self.checkFrame,text="Use Y", variable=self.yVar) self.scr2Check.grid(row = 4, column = 0) self.scr2Check = Checkbutton(self.checkFrame,text="Use Z", variable=self.zVar) self.scr2Check.grid(row = 5, column = 0) # --- Listboxes --- self.listLabel2 = Label(self.ListFrame2, text="Select morph(s)" , anchor=N, justify=LEFT) self.listLabel2.grid(row=1, column=1) self.ListScroll2 = Scrollbar(self.ListFrame2, orient=VERTICAL) # Morphs self.ListScroll2.grid( row=2, column=0,sticky=N+S+E) self.List2 = Listbox(self.ListFrame2, height=20, width=20, selectmode=MULTIPLE,exportselection=0, yscrollcommand=self.ListScroll2.set) self.List2.grid( row=2, column=1) self.ListScroll2["command"] = self.List2.yview # --- Entries --- self.cycsLabel = Label(self.buttonFrame, text="Max Repititions:" , anchor=N, justify=LEFT) self.cycsLabel.grid(row=4, column=0) self.entryCycs = Entry(self.buttonFrame, width=10) self.entryCycs.insert(0,"1000") self.entryCycs.grid(row=5,column=0) self.angLabel = Label(self.buttonFrame, text="Threshold:" , anchor=N, justify=LEFT) self.angLabel.grid(row=6, column=0) self.entryAng = Entry(self.buttonFrame, width=10) self.entryAng.insert(0,"0.0015") self.entryAng.grid(row=7,column=0) # --- Buttons --- self.buttonRun = Button(self.buttonFrame, text="Run", command=self.handleRun) self.buttonRun.grid(row=8, column=0) self.buttonQuit = Button(self.buttonFrame, text="Quit", command=self.die) self.buttonQuit.grid(row=9, column=0) self.fill_boxes() def die(self): """End the script""" root.destroy() root.quit() def fill_boxes(self): act = scene.CurrentActor() for p in act.Parameters(): if p.IsMorphTarget(): self.List2.insert(END,p.Name()) def handleRun(self): s = self.List2.curselection() if s != (): morphs = [self.List2.get(int(c)) for c in s] else: morphs = [] screen = self.scrVar.get() reps = self.entryCycs.get() highlow = self.hiloVar.get() x = self.xVar.get() y = self.yVar.get() z = self.zVar.get() screenlist = [x,y,z] try: reps = int(reps) except ValueError: reps = 1000 threshold = self.entryAng.get() try: threshold = float(threshold) except ValueError: threshold = 0.0015 run(screen,reps,threshold,morphs,highlow,screenlist) root.destroy() root.quit() app = App(root) root.mainloop()