from Tkinter import * import poser #import Numeric#, time, os try: import Numeric except: import numpy as Numeric Numeric.Int = Numeric.int Numeric.Float = Numeric.float Numeric.matrixmultiply = Numeric.dot """ This is a very basic Tkinter-driven UV editor for use within Poser. It doesn't apply any new mapping types and is intended for small alterations. It will try to identify backwards facing texpolys and can weld or split texvertices, as well as translate them. It can create a new copy of the current actor, with the altered UVs applied, but the new actor will not inherit any groups from the original, nor will it have materials settings copied over. """ """ To do: -x- User re-define scale -x- Hide/show by material, group, poly facing. Show all. -x- Load two actors, use second for background/comparison and for .vwt handling --- Polygon select by facing direction --- Polygon handling modes - allow de-selection of only verts of selected poly(s), move entire poly(s) (splits texverts) --- .vwt load - convert UVs and rearrange existing verts and polys --- Create new .vwt from altered UVs (conceptual problem with favoring some split texverts over others). --- Screen for valid texvert welds, using existing vuv tags --- Tighten function for selected verts --- Move selected vert(s) to nearest corner or edge of overlapping background actor tri/poly --- Need a way to move overlapped verts of same poly away from one another, to allow manual handling. --- Is there a better way to correct slow scrollbar handling with large meshes? --- Try to constrain visible area during zoom - currently jumps --- Real-time vuv coloring display (current approach too slow for real time, with large meshes) --- Hide current poly user selection --- Need split handling for texverts when neighbors are hidden """ scene = poser.Scene() #scale = 750 # Scale the UVs and the display area def run_select(): """Select actor(s) and scale, then run the UV editor""" scale = 750 #500 names = ['GROUND','FocusDistanceControl'] remlist = [act.Name() for act in scene.Actors() if ((act.Name() in names) or ("" in act.Name()) or (act.IsDeformer()) or (act.IsZone()) or (act.IsBase()) or (act.Geometry() == None) or ('CenterOfMass' in act.Name()) or (act.IsLight()) or (act.IsCamera()) or ('MatSphere' in act.Name()))] thelist = ["%s,None" %(act.Name()) for act in scene.Actors() if (act.ItsFigure() == None) and (act.Name() not in remlist)] l2 = ["%s,%s" %(act.Name(),act.ItsFigure().Name()) for act in scene.Actors() if ((act.ItsFigure() != None) and (act.Geometry() != None)) and (not 'CenterOfMass' in act.Name())] thelist = thelist + l2 testing = poser.DialogSimple.AskMenu("Pick actor","To display",thelist) if testing != None and testing != "": thelist.remove(testing) testing = testing.split(",") if testing[1] != "None": shrinkObj = scene.Figure(testing[1]).Actor(testing[0]) else: shrinkObj = scene.Actor(testing[0]) else: return """ testing = poser.DialogSimple.AskMenu("Pick actor","To compare",thelist) if testing != None and testing != "": testing = testing.split(",") if testing[1] != "None": wrapObj = scene.Figure(testing[1]).Actor(testing[0]) else: wrapObj = scene.Actor(testing[0]) else: wrapObj = None # Allow no selection for this (will prevent use of .vwt functions) """ wrapObj = None # Temp """ scale = poser.DialogSimple.AskInt("Enter scale size") if scale == None: scale = 750 # Apply a low default """ uvs = UVs(shrinkObj.Geometry()) puv, texpolys = polyverts(shrinkObj.Geometry(),shrinkObj.Geometry().TexPolygons(),UV=1) pverts, mats, groups = polyverts(shrinkObj.Geometry(),shrinkObj.Geometry().Polygons()) #global root root = Tk() root.geometry('+50+50') # Position the toplevel window #print len(puv), len(texpolys) #return app = App(root,shrinkObj,wrapObj,scale,uvs,puv,texpolys,mats,groups,pverts) #app.refreshIt() # Causes apparently focus-related crash problems when used with Tk Menu widget. root.mainloop() del app def UVs(geom): """ Organize the UV data into a Numeric array """ uvverts = [i for i in geom.TexVertices()] uvs = [[] for i in geom.TexVertices()] for i in range(len(uvverts)): tv = uvverts[i] uvs[i] = [tv.U(),(-tv.V()+1),tv.V()] # Need to flip the v coords and translate them back to the proper display quadrant uvverts = [] return uvs def polyverts(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: puvverts = [] texpolys = [] gset = geom.TexSets() else: pverts = [] mats = [] groups = [] gset = geom.Sets() for p in polys: if UV: l = p.NumTexVertices() else: l = p.NumVertices() s = p.Start() e = s + l vl = [v for v in gset[s:e]] if UV: puvverts.append(vl) if len(vl) > 2: texpolys.append([s,l]) # We can build this here and keep it, because it won't change. else: pverts.append(vl) mats.append(p.MaterialName()) groups.append(tuple([i for i in p.Groups()])) if UV: return puvverts,texpolys else: return pverts,mats,groups class App(object): def __init__(self,master,act1,act2,scale,tverts,puv,texpolys,mats,groups,pverts): self.master = master # You will obey me... you will obey... ME! master.title("UV Editor Test") self.master.resizable(width=1,height=1) # Frames self.picframe = Frame(self.master,borderwidth=10,relief=RIDGE) self.picframe.grid(row=0,column=0) self.menuframe = Frame(self.master,borderwidth=2,relief=RIDGE) self.menuframe.grid(row=1,column=0) self.buttonframe = Frame(self.master,borderwidth=2,relief=RIDGE) self.buttonframe.grid(row=2,column=0) self.listframe = Frame(self.master,borderwidth=0) #self.listframe.grid(row=0,column=2) self.listframe2 = Frame(self.listframe,borderwidth=0) self.listframe2.grid(row=0,column=0) self.listbuttonframe = Frame(self.listframe,borderwidth=0) self.listbuttonframe.grid(row=1,column=0) # Menu bar self.bar = Menu(self.menuframe) self.filem = Menu(self.bar,tearoff=0) self.filem.add_command(label="Quit",command=self.die) self.filem.add_command(label="Make Copy",command=self.make_copy2) self.funcm = Menu(self.bar,tearoff=0) self.funcm.add_command(label="Weld",command=self.weld) self.funcm.add_command(label="Split",command=self.split) self.funcm.add_command(label="Find shared",command=self.pick_vuv2) self.viewm = Menu(self.bar,tearoff=0) self.viewm.add_command(label="Zoom +",command=self.zoom_up) self.viewm.add_command(label="Zoom -",command=self.zoom_down) self.optm = Menu(self.bar,tearoff=0) self.optm.add_command(label="Blind scrolling ON",command=self.blind_scroll_on) self.optm.add_command(label="Blind scrolling OFF",command=self.blind_scroll_off) self.hidem = Menu(self.bar,tearoff=0) self.sidem = Menu(self.hidem,tearoff=0) self.sidem.add_command(label="Hide Front",command=self.hide_front) self.sidem.add_command(label="Hide Back",command=self.hide_back) self.sidem.add_command(label="Show Front",command=self.show_front) self.sidem.add_command(label="Show Back",command=self.show_back) self.hidem.add_command(label="Materials",command=self.grid_mats) self.hidem.add_command(label="Groups",command=self.grid_groups) self.hidem.add_cascade(label="Facing",menu=self.sidem) self.hidem.add_command(label="Show All",command=self.show_all) self.hidem.add_command(label="Invert",command=self.show_invert) self.bar.add_cascade(label="File", menu=self.filem) self.bar.add_cascade(label="View",menu=self.viewm) self.bar.add_cascade(label="Functions",menu=self.funcm) self.bar.add_cascade(label="Options", menu=self.optm) self.bar.add_cascade(label="Hide/Show",menu=self.hidem) self.master.config(menu=self.bar) # ---- Lists ---- self.ListScroll = Scrollbar(self.listframe2, orient=VERTICAL) # Materials self.List = Listbox(self.listframe2, height=20, width=20, selectmode=MULTIPLE,exportselection=0, yscrollcommand=self.ListScroll.set) self.ListScroll["command"] = self.List.yview self.ListScroll2 = Scrollbar(self.listframe2, orient=VERTICAL) # Groups self.List2 = Listbox(self.listframe2, height=20, width=20, selectmode=MULTIPLE,exportselection=0, yscrollcommand=self.ListScroll2.set) self.ListScroll2["command"] = self.List2.yview # Scrollbars self.picScrollv = Scrollbar(self.picframe, orient=VERTICAL) self.picScrollv.grid( row=0, column=1,sticky=N+S+E) self.picScrollh = Scrollbar(self.picframe, orient=HORIZONTAL) self.picScrollh.grid( row=1, column=0,sticky=E+W+S) # Canvases self.canvas = Canvas(self.picframe,width=scale,height=scale,bg="white",closeenough=4.0, yscrollcommand=self.picScrollv.set,xscrollcommand=self.picScrollh.set, yscrollincrement=0,xscrollincrement=0) self.blank = Canvas(self.picframe,width=scale,height=scale,bg="white") # A size-holder proxy for "blind scrolling" # Buttons self.buttonQuit = Button(self.buttonframe, text="Quit", command=self.die) self.buttonQuit.grid(row=0, column=0) self.buttonSplit = Button(self.buttonframe, text="Split", command=self.split) self.buttonSplit.grid(row=0, column=1) self.buttonWeld = Button(self.buttonframe, text="Weld", command=self.weld) self.buttonWeld.grid(row=0, column=2) self.buttonZoomup = Button(self.buttonframe, text="Zoom +", command=self.zoom_up) self.buttonZoomup.grid(row=0, column=5) self.buttonZoomdn = Button(self.buttonframe, text="Zoom -", command=self.zoom_down) self.buttonZoomdn.grid(row=0, column=6) self.buttonMake = Button(self.buttonframe, text="Make\nCopy", command=self.make_copy2) self.buttonMake.grid(row=0, column=7) self.buttonShared = Button(self.buttonframe, text="Find\nShared", command=self.pick_vuv2) self.buttonShared.grid(row=0, column=8) self.buttonSMats = Button(self.listbuttonframe,text="Show",command=self.show_mats) self.buttonHMats = Button(self.listbuttonframe,text="Hide",command=self.hide_mats) self.buttonMatsCancel = Button(self.listbuttonframe,text="Cancel",command=self.ungrid_mats) self.buttonSGrps = Button(self.listbuttonframe,text="Show",command=self.show_groups) self.buttonHGrps = Button(self.listbuttonframe,text="Hide",command=self.hide_groups) self.buttonGrpsCancel = Button(self.listbuttonframe,text="Cancel",command=self.ungrid_groups) # Bindings self.picScrollv["command"] = self.canvas.yview self.picScrollh["command"] = self.canvas.xview self.canvas.grid(row=0,column=0) self.canvas.bind("",self.click) self.canvas.bind("",self.unclick) self.canvas.bind("",self.move) self.canvas.bind("",self.addselect) self.canvas.bind("",self.addsel_pass) self.canvas.bind("",self.dragsel) self.canvas.bind("",self.dragsel_stop) #self.master.protocol('WM_DELETE_WINDOW', self.die) # Can't use this approach together with binding changes in blind scrolling - P7 crashes. # Class variables self.texpolys = texpolys # Store this one for later self.oldx = 0.0 # Store the mouse positions self.oldy = 0.0 self.mode = "vert" # Select verts or polys self.click = 0 # Switch for moving by drag, on/off self.drag = None # Selection box variable self.dragcorner = () # Pinned start corner for selection box self.numverts = len(tverts) # Keep track of number of texverts, for appending new splits self.vertscale = 1.0 # Size of vertex - number of pixels from each side to center self.scale = scale # Scale of canvas in pixels, used to scale up UV values and positions self.zoom = [0,1.0] # Keep track of zooming. zoom[0] tracks number of times up or down; zoom[1] tracks the scaling factor for scroll limits self.side = "front" # Work with forward or backward-facing polys self.useside = 1 # On/off for automatic backfacing checks self.act = act1 # The actor on whose UVs we work self.bact = act2 # Comparison actor (for background display or .vwt handling) self.zoomproxy = self.canvas.create_rectangle(0.0,0.0,0.0,0.0,fill="",outline="") # A proxy rectangle to compensate for zoom sizing self.vdata = [[[] for j in i] for i in puv] # A list to track texvertex data - necessary to speed up the save process, unfortunately.... self.pdata = [[1,"front",mats[i],[j for j in groups[i]]] for i in range(len(puv)) if len(puv[i]) > 2] # Poly data - tracking for hide-show functions temp = [] # Fill the hide-show listboxes for mat in mats: if not mat in temp: temp.append(mat) for mat in temp: self.List.insert(END,mat) temp = [] for grouptuple in groups: for group in grouptuple: if not group in temp: temp.append(group) for group in temp: self.List2.insert(END,group) self.boundaries() # Create the gray outer bounds (< 0.0, > 1.0) - also initializes canvas scrollregions self.draw(tverts,puv,pverts) # Build the verts and polys def _pass(self): """Blank temp for WIP buttons and menus""" pass def grid_mats(self): """Show the materials listbox and buttons for hide-show""" self.listframe.grid(row=0,column=2) self.ListScroll.grid(row=0,column=0,sticky=N+S+E) self.List.grid(row=0,column=1) self.buttonSMats.grid(row=0,column=0) self.buttonHMats.grid(row=0,column=1) self.buttonMatsCancel.grid(row=0,column=2) def ungrid_mats(self): """Hide the materials listbox and buttons for hide-show""" self.ListScroll.grid_forget() self.List.grid_forget() self.buttonSMats.grid_forget() self.buttonHMats.grid_forget() self.buttonMatsCancel.grid_forget() self.listframe.grid_forget() def grid_groups(self): """Show the groups listbox and buttons for hide-show""" self.listframe.grid(row=0,column=2) self.ListScroll2.grid(row=0,column=0,sticky=N+S+E) self.List2.grid(row=0,column=1) self.buttonSGrps.grid(row=0,column=0) self.buttonHGrps.grid(row=0,column=1) self.buttonGrpsCancel.grid(row=0,column=2) def ungrid_groups(self): """Hide the groups listbox and buttons for hide-show""" self.ListScroll2.grid_forget() self.List2.grid_forget() self.buttonSGrps.grid_forget() self.buttonHGrps.grid_forget() self.buttonGrpsCancel.grid_forget() self.listframe.grid_forget() def show_mats(self): self.hide_show(0) def hide_mats(self): self.hide_show(1) def show_groups(self): self.hide_show(2) def hide_groups(self): self.hide_show(3) def show_front(self): self.hide_show(4) def hide_front(self): self.hide_show(5) def show_back(self): self.hide_show(6) def hide_back(self): self.hide_show(7) def hide_show_set(self,use,p_i,back=0): """Applies the actual transparency and accessibility settings for hide-show""" if use == "show": val = 1 col = "black" remtag = "off" addtag = "on" if back: # Don't need self.useside check: back won't be set if useside == 0, so this case won't occur col2 = "blue" else: col2 = col if use == "hide": val = 0 col = "" remtag = "on" addtag = "off" col2 = col self.pdata[p_i][0] = val # Change pdata visibility lookup listing p = self.polys[p_i] self.canvas.itemconfigure(p,outline=col2) # Set poly transparency for vi in self.vdata[p_i]: # Handle verts of poly v = self.verts[vi[0]] self.canvas.itemconfigure(v,fill=col,outline=col) # Set vert transparency self.canvas.dtag(v,remtag) # Remove old tag self.canvas.addtag_withtag(addtag,v) # Set new tag def show_all(self): """Restore visibility for all polys and verts""" for p_i in range(len(self.pdata)): pd = self.pdata[p_i] if pd[0] == 0: back = 0 if pd[1] == "back": back = 1 self.hide_show_set("show",p_i,back=back) def show_invert(self): """Invert the visibility settings for polys and verts""" screen = [i[0] for i in self.pdata] if screen.count(0) == 0: # Don't invert if nothing is hidden return for p_i in range(len(self.pdata)): pd = self.pdata[p_i] if pd[0] == 0: back = 0 if pd[1] == "back": back = 1 self.hide_show_set("show",p_i,back=back) elif pd[0] == 1: self.hide_show_set("hide",p_i) def hide_show(self,use): """Hide or show specified polys and verts""" back = 0 run = "hide" if use % 2 == 0: run = "show" if use == 0 or use == 1: cursel = [self.List.get(i) for i in self.List.curselection()] screen = [p_i for p_i in range(len(self.pdata)) if self.pdata[p_i][2] in cursel] self.ungrid_mats() if use == 2 or use == 3: cursel = [self.List2.get(i) for i in self.List2.curselection()] screen = [] for p_i in range(len(self.pdata)): for group in self.pdata[p_i][3]: if group in cursel: screen.append(p_i) break self.ungrid_groups() if use > 3: if use == 4 or use == 5: facing = "front" if use == 6 or use == 7: facing = "back" back = 1 screen = [p_i for p_i in range(len(self.pdata)) if self.pdata[p_i][1] == facing] for p_i in screen: self.hide_show_set(run,p_i,back=back) def blind_scroll_on(self): """Activate blind scrolling, to compensate for slow scrollbars when displaying a high-res mesh. This turns off the UV display canvas and shows a blank canvas which exists only to preserve the proper frame dimensions while the main Canvas is hidden.""" self.picScrollv.bind("",self.ungrid) self.picScrollv.bind("",self.regrid) self.picScrollh.bind("",self.ungrid) self.picScrollh.bind("",self.regrid) def blind_scroll_off(self): """Turns off blind scrolling""" self.picScrollv.unbind("") self.picScrollv.unbind("") self.picScrollh.unbind("") self.picScrollh.unbind("") def ungrid(self,event): """Blind scrolling grab function""" self.blank.grid(row=0,column=0) self.canvas.grid_remove() def regrid(self,event): """Blind scrolling release function""" self.canvas.grid(row=0,column=0) self.blank.grid_remove() def click(self,event): """Click on a vert or poly to select it.""" v = self.canvas.canvasx(event.x),self.canvas.canvasy(event.y) clicked = self.canvas.find_overlapping(v[0],v[1],v[0],v[1]) if clicked == (): # Clear the selection if user clicks in whitespace sels = self.canvas.find_withtag("selected") for item in sels: tags = self.canvas.gettags(item) if tags[0] == "vert": self.canvas.dtag(item,"selected") self.canvas.itemconfigure(item,fill="black",outline="black") self.unpick_vuv(item) # if tags[0] == "poly": self.canvas.dtag(item,"selected") self.canvas.itemconfigure(item,fill="",outline="black") else: # Move the selection self.click = 1 for item in clicked: tags = self.canvas.gettags(item) if not "selected" in tags: if tags[0] == self.mode: if self.mode == "vert": if "on" in tags: # Only select visible verts self.canvas.dtag(item,"welds") self.canvas.itemconfigure(item,fill="red",outline="red") self.canvas.addtag_withtag("selected",item) #self.pick_vuv(item) # Potentially slow else: self.numverts += 1 # Try to cover vert split handling.... if self.mode == "poly": self.canvas.itemconfigure(item,fill="",outline="red") self.oldx = self.canvas.canvasx(event.x) self.oldy = self.canvas.canvasy(event.y) def dragsel(self,event): """Hold down ctrl and drag to create a selection box""" if self.drag == None: v = self.canvas.canvasx(event.x),self.canvas.canvasy(event.y) self.drag = self.canvas.create_rectangle(v[0],v[1],v[0],v[1],fill="",outline="green") self.dragcorner = v def dragsel_stop(self,event): """Turns off the ctrl-drag selection box""" self.canvas.delete(self.drag) self.drag = None def dragsel_select(self,event): """Draw the selection box and add selections""" co = self.dragcorner v = self.canvas.canvasx(event.x),self.canvas.canvasy(event.y) self.canvas.coords(self.drag,co[0],co[1],v[0],v[1]) clicked = self.canvas.find_overlapping(co[0],co[1],v[0],v[1]) if clicked == (): return if self.drag in clicked: clicked = list(clicked) clicked.remove(self.drag) for item in clicked: tags = self.canvas.gettags(item) if not "selected" in tags: # Select if tags[0] == self.mode: if self.mode == "vert": if "on" in tags: self.canvas.itemconfigure(item,fill="red",outline="red") self.canvas.addtag_withtag("selected",item) if self.mode == "poly": self.canvas.itemconfigure(item,fill="",outline="red") # Needs to remove selections if box leaves them.... def addselect(self,event): """Hold down shift to extend a click selection""" v = self.canvas.canvasx(event.x),self.canvas.canvasy(event.y) clicked = self.canvas.find_overlapping(v[0],v[1],v[0],v[1]) for item in clicked: tags = self.canvas.gettags(item) if not "selected" in tags: # Select if tags[0] == self.mode: if self.mode == "vert": if "on" in tags: self.canvas.dtag(item,"welds") # self.canvas.itemconfigure(item,fill="red",outline="red") self.canvas.addtag_withtag("selected",item) #self.pick_vuv(item) # this is potentially slow if self.mode == "poly": self.canvas.itemconfigure(item,fill="",outline="red") else: # De-select if tags[0] == self.mode: if self.mode == "vert": if "on" in tags: self.canvas.dtag(item,"selected") self.canvas.itemconfigure(item,fill="black",outline="black") self.unpick_vuv(item) # if self.mode == "poly": self.canvas.itemconfigure(item,fill="",outline="black") def addsel_pass(self,event): """A blank binding event to lock off mouse release with shift""" pass def pick_vuv(self,item): # """Auto-pick true vert companions - this is slow with large meshes""" tags = self.canvas.gettags(item) tvn = self.canvas.find_withtag(tags[5]) for tv in tvn: if tv != item: tvtags = self.canvas.gettags(tv) if not "selected" in tvtags: self.canvas.itemconfigure(tv,fill="green",outline="green") self.canvas.addtag_withtag("welds",tv) def pick_vuv2(self): """Find all true vert companions with a button or menu selection""" sels = self.canvas.find_withtag("selected") for item in sels: tags = self.canvas.gettags(item) tvn = self.canvas.find_withtag(tags[5]) for tv in tvn: if not tv in sels: self.canvas.itemconfigure(tv,fill="green",outline="green") self.canvas.addtag_withtag("welds",tv) def unpick_vuv(self,item): # """Remove true vert companion display coloring and internal tags""" #self.canvas.dtag(item,"welds") # Actual item color handling is in functions which call this tags = self.canvas.gettags(item) tvn = self.canvas.find_withtag(tags[5]) for tv in tvn: tvtags = self.canvas.gettags(tv) #if "welds" in tvtags: # Use of "in" is potentially slow if len(tvtags) > 7: if tvtags[7] == "welds":#!= "selected": # tvtags[7] will be selected or welds col = "black" if tvtags[6] == "off": col = "" self.canvas.dtag(tv,"welds") self.canvas.itemconfigure(tv,fill=col,outline=col) def move(self,event): """Drag selected verts in real time""" if not self.click: if self.drag: self.dragsel_select(event) # Run the Ctrl-drag selection box return self.oldx = self.canvas.canvasx(event.x) self.oldy = self.canvas.canvasy(event.y) return sels = self.canvas.find_withtag("selected") for item in sels: tags = self.canvas.gettags(item) if tags[0] == self.mode: if self.mode == "vert": self.canvas.move(item,self.canvas.canvasx(event.x)-self.oldx,self.canvas.canvasy(event.y)-self.oldy) self.refresh_poly2(item,config=0,sidecheck=0) # Adjust the poly, don't check normals while moving if self.mode == "poly": pass self.oldx = self.canvas.canvasx(event.x) self.oldy = self.canvas.canvasy(event.y) def unclick(self,event): """Release event; moves verts and replaces old polys with new""" if not self.click: if self.drag: self.dragsel_stop(None) # If ctrl is released before mouse button with dragsel selection box return # If self.click isn't set, there is no valid selection self.click = 0 sels = self.canvas.find_withtag("selected") for item in sels: tags = self.canvas.gettags(item) if tags[0] == self.mode: if self.mode == "vert": p_i = int(tags[1].replace("vpoly","")) vpi = int(tags[3].replace("vpi","")) poly = self.polys[p_i] p = self.canvas.coords(poly) v = self.canvas.coords(item) polytags = self.canvas.gettags(poly) color = "black" side = self.side if self.useside: side = self.texpolynorms3(tags[1],use=1) if side == "back": color = "blue" self.pdata[p_i][1] = side # Update poly data list for hide-show tags2 = polytags[0],polytags[1],side uv = [v[0]+self.vertscale, v[1]+self.vertscale] p[vpi*2] = uv[0] p[(vpi*2)+1] = uv[1] self.canvas.coords(poly,tuple(p)) self.canvas.itemconfigure(poly,fill="",outline=color,tags=tags2) # Update the vdata list uv = self.fix_uvs(uv) self.vdata[p_i][vpi][3] = uv[0] self.vdata[p_i][vpi][4] = uv[1] self.canvas.dtag(item,"selected") self.canvas.itemconfigure(item,fill="black",outline="black") self.unpick_vuv(item) # if self.mode == "poly": pass def move_split(self,item,tags): """Need to split hidden verts from visible when a visible vert is moved away from welded companions which are hidden.""" p_i = int(tags[1].replace("vpoly","")) vpi = int(tags[3].replace("vpi","")) self.numverts += 1 tags = tags[0],tags[1],"vert%s"%(self.numverts),tags[3],tags[4],tags[5],tags[6] self.canvas.itemconfigure(item,tags=tags) self.vdata[p_i][vpi] = self.numverts # Update vdata list def weld(self): self.weldsplit(0) def split(self): self.weldsplit(1) def weldsplit(self,runtype): """Weld or split selected texvertices""" # Weld should only work on texverts which have the same vertex association! # Should never work on multiple texverts in same poly - would corrupt the poly to texpoly relationship # Later: # Actually, illegal welds are being ruled out more or less accidentally, but a polygon can have its # texverts collapsed upon one another using weld, which is currently hard to repair.... sels = self.canvas.find_withtag("selected") selset = set(sels) tosplit = set() for item in sels: pos = self.canvas.coords(item) overlapped = set(self.canvas.find_overlapping(pos[0],pos[1],pos[2],pos[3])) overlapped.remove(item) overlapped = selset.intersection(overlapped) tosplit = tosplit.union(overlapped) if len(tosplit) == 0: return index = 500000000 for item in tosplit: # Find lowest index to use as merged index tags = self.canvas.gettags(item) vindex = int(tags[2].replace("vert","")) if vindex < index: index = vindex if runtype == 0: # Weld uv = [0.0,0.0] for item in tosplit: tags = self.canvas.gettags(item) #self.vdata[int(tags[1].replace("vpoly",""))][int(tags[3].replace("vpi",""))] = index # Update vdata list tags = tags[0],tags[1],"vert%s"%(index),tags[3],tags[4],tags[5],tags[6] self.canvas.itemconfigure(item,fill="black",outline="black",tags=tags) coords = self.canvas.coords(item) uv[0] += (coords[0]+self.vertscale) uv[1] += (coords[1]+self.vertscale) uv[0] /= len(tosplit) uv[1] /= len(tosplit) for item in tosplit: coords = self.canvas.coords(item) delta = [uv[0]-(coords[0]+self.vertscale),uv[1]-(coords[1]+self.vertscale)] self.canvas.move(item,delta[0],delta[1]) self.refresh_poly2(item,weldsplit=1) if runtype == 1: # Split for item in tosplit: tags1 = self.canvas.gettags(item) self.numverts += 1 tags = self.canvas.gettags(item) #self.vdata[int(tags[1].replace("vpoly",""))][int(tags[3].replace("vpi",""))] = self.numverts # Update vdata list tags = tags[0],tags[1],"vert%s"%(self.numverts),tags[3],tags[4],tags[5],tags[6] self.canvas.itemconfigure(item,fill="black",outline="black",tags=tags) polynum = tags1[1] tpverts = self.canvas.find_withtag(polynum) uv = [0.0,0.0] for v in tpverts: coords = self.canvas.coords(v) uv[0] += (coords[0]+self.vertscale) uv[1] += (coords[1]+self.vertscale) uv[0] /= len(tpverts) uv[1] /= len(tpverts) coords = self.canvas.coords(item) delta = [uv[0]-(coords[0]+self.vertscale),uv[1]-(coords[1]+self.vertscale)] amt = 0.05 self.canvas.move(item,delta[0]*amt,delta[1]*amt) self.refresh_poly2(item,weldsplit=1) def refresh_poly2(self,item,config=1,sidecheck=1,weldsplit=0): """Refresh the polygon shape for specified vertex - used by weldsplit""" tags = self.canvas.gettags(item) p_i = int(tags[1].replace("vpoly","")) vpi = int(tags[3].replace("vpi","")) poly = self.polys[p_i] p = self.canvas.coords(poly) v = self.canvas.coords(item) polytags = self.canvas.gettags(poly) color = "black" side = self.side if self.useside and sidecheck: side = self.texpolynorms3(tags[1],use=1) if side == "back": color = "blue" self.pdata[p_i][1] = side # Update poly data list for hide-show tags2 = polytags[0],polytags[1],side uv = [v[0]+self.vertscale, v[1]+self.vertscale] p[vpi*2] = uv[0] p[(vpi*2)+1] = uv[1] self.canvas.coords(poly,tuple(p)) self.canvas.itemconfigure(poly,fill="",outline=color,tags=tags2) # Update the vdata list uv = self.fix_uvs(uv) if weldsplit: self.vdata[p_i][vpi][1] = int(tags[2].replace("vert","")) self.vdata[p_i][vpi][3] = uv[0] self.vdata[p_i][vpi][4] = uv[1] if config: self.canvas.dtag(item,"selected") self.canvas.itemconfigure(item,fill="black",outline="black",tags=tags) self.unpick_vuv(item) # def fix_uvs(self,uv): """Get the actual UV coordinates, to log to the vdata list""" zoomvar = self.zoom[0] if zoomvar: # Use zoomproxy to find un-zoomed coordinates item = self.zoomproxy self.canvas.coords(item,uv[0],uv[1],uv[0],uv[1]) if zoomvar > 0: # Un-zoom for i in range(zoomvar): self.canvas.scale(item,0.0,0.0,0.5,0.5) if zoomvar < 0: for i in range(abs(zoomvar)): self.canvas.scale(item,0.0,0.0,2.0,2.0) coords = self.canvas.coords(item) self.canvas.coords(item,0.0,0.0,0.0,0.0) uv = [coords[0],coords[1]] uv = [uv[0]/self.scale,uv[1]/self.scale] # De-scale uv = [uv[0],uv[1]-1.0] # Move back into flipped quadrant return [uv[0],-uv[1]] # Flip v def draw(self,tverts,puv,pverts): """Creates the verts and polys""" scale = self.scale self.polys = [] # List to store polys for p_i in range(len(puv)): if len(puv[p_i]) > 2: # Polygon requires at least three corners p = [] for i in puv[p_i]: p.append((tverts[i][0] * scale)) p.append((tverts[i][1] * scale)) color = "black" side = self.side if self.useside: side = self.texpolynorms3(p) # Get forward/backward facing if side == "back": color = "blue" self.pdata[p_i][1] = side # Update poly data list for hide-show tags = "poly","poly%s" %(p_i),side self.polys.append(self.canvas.create_polygon(p,fill="",outline=color,tags=tags)) #else: #continue #print p_i,puv[p_i],p self.verts = [] # List to store verts - all verts are pre-split vs = self.vertscale for p_i in range(len(puv)): p = puv[p_i] for vii in range(len(p)): vi = p[vii] v = [(i * scale) for i in tverts[vi]] uv = [tverts[vi][0],tverts[vi][2]] self.vdata[p_i][vii] = [len(self.verts),vi,pverts[p_i][vii],uv[0],uv[1]] # index into self.verts, internal vert association, true vertex association, uv coords tags = "vert","vpoly%s" %(p_i),"vert%s"%(vi),"vpi%s"%(vii),"index%s" %(len(self.verts)),"vuv%s"%(pverts[p_i][vii]),"on" self.verts.append(self.canvas.create_rectangle(v[0]-vs,v[1]-vs,v[0]+vs,v[1]+vs,fill="black",tags=tags)) def boundaries(self): """Create background for areas out of UV bounds""" sc = self.scale gray = "#E4E4E4" self.bounds = [] b = [0.0,0.0,1.0*sc,1.0*sc] self.bounds.append(self.canvas.create_rectangle(b[0]-sc,b[1]-sc,b[2]-sc,b[3]+sc,fill=gray,tags=("bounds","west"))) self.bounds.append(self.canvas.create_rectangle(b[0]-sc,b[1]-sc,b[2]+sc,b[3]-sc,fill=gray,tags=("bounds","north"))) self.bounds.append(self.canvas.create_rectangle(b[0]+sc,b[1]-sc,b[2]+sc,b[3]+sc,fill=gray,tags=("bounds","east"))) self.bounds.append(self.canvas.create_rectangle(b[0]-sc,b[1]+sc,b[2]+sc,b[3]+sc,fill=gray,tags=("bounds","south"))) self.set_scrollbounds() # Set up scrollregions def set_scrollbounds(self): """Define the canvas scrollregions""" self.canvas.update_idletasks() # Not sure we need this.... h = self.canvas.winfo_reqheight() * self.zoom[1] w = self.canvas.winfo_reqwidth() * self.zoom[1] self.canvas["scrollregion"] = (-w,-h,2.0*w,2.0*h) def zoom_up(self): self.zooming(2.0,1) def zoom_down(self): self.zooming(0.5,-1) def zooming(self,amt,factor): """Zoom function - actually scales the components""" for item in self.verts: # Keep the vertscale the same self.canvas.scale(item,0.0,0.0,amt,amt) coords = self.canvas.coords(item) uv = coords[0]+(self.vertscale*amt), coords[1]+(self.vertscale*amt) self.canvas.coords(item,uv[0]-self.vertscale,uv[1]-self.vertscale,uv[0]+self.vertscale,uv[1]+self.vertscale) for item in self.polys: self.canvas.scale(item,0.0,0.0,amt,amt) for item in self.bounds: self.canvas.scale(item,0.0,0.0,amt,amt) #for item in self.background: #self.canvas.scale(item,0.0,0.0,amt,amt) self.zoom[0] += factor self.zoom[1] *= amt #self.vertscale *= amt self.set_scrollbounds() # Update scrollregions def die(self): """Quit function - break out of the Master's hypnotic spell""" #root.destroy() #root.quit() self.master.destroy() self.master.quit() def texpolynorms3(self,p0,use=0): """Use the "best fit" normal approach""" if use: # Using texvert coords - p is polynum vs = self.vertscale p1 = self.canvas.find_withtag(p0) p2 = [self.canvas.coords(i) for i in p1] p2 = [[p2[i][0]+vs,p2[i][1]+vs,0.0] for i in range(len(p2))] else: # Run at startup - p is flattened(?) poly list p2 = [[p0[i],p0[i+1],0.0] for i in range(len(p0)) if i % 2==0] result = 0.0 for i in range(len(p2)): c = p2[i] if i == 0: p = p2[len(p2)-1] # Start with previous as last else: p = p2[i-1] result += (p[1] + c[1]) * (p[0] - c[0]) # Only need the z coordinate if result > 0.0: # Forward and backward are reversed because we flipped vertically for Tk display. return "back" else: return "front" def make_copy2(self): """ Create a new mesh with altered UVs. Uses the vdata list to speed the process. Could also be used to change current actor geometry, by calling self.build_geom() with copy=0. The texpolys list is generated at the start and retained for use here, as it shouldn't change. vdata[vi] = [index into self.verts, internal vert association, true vertex association, u coord, v coord] """ scale = self.scale screen = [-1 for i in range(self.numverts+1)] uvs = [] sets = [] for p_i in range(len(self.vdata)): pvt = self.vdata[p_i] pv = [-1 for i in pvt] for vpi in range(len(pvt)): v = pvt[vpi] vindex = v[1] if screen[vindex] == -1: uvs.append([v[3],v[4]]) luv = len(uvs)-1 pv[vpi] = luv screen[vindex] = luv else: pv[vpi] = screen[vindex] for v in pv: sets.append(v) uvs = Numeric.array(uvs,Numeric.Float) polys = Numeric.array(self.texpolys,Numeric.Int) sets = Numeric.array(sets,Numeric.Int) self.build_geom(self.act,uvs,sets,polys,copy=1) def build_geom(self,act,tverts,tsets,tpolys,copy=0): """ Make a new geometry with the altered UVs. This new copy will not port over any of the groups existing in the original geometry. If copy == 1, a fresh actor will be created using the new geometry. If copy == 0, the submitted actor's geometry will be changed. """ geom = act.Geometry() 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) newgeom = poser.NewGeometry() newgeom.AddGeneralMesh(polys,sets,verts,tpolys,tsets,tverts) for mat in geom.Materials(): newgeom.AddMaterialName(mat.Name()) for pi in range(geom.NumPolygons()): poly = geom.Polygon(pi) newpoly = newgeom.Polygon(pi) newpoly.SetMaterialIndex(poly.MaterialIndex()) if copy: newprop = scene.CreatePropFromGeom(newgeom,"%s_new_UVs" %(act.Name())) scene.SelectActor(newprop) else: act.SetGeometry(newgeom) act.MarkGeomChanged() run_select()