import poser import Numeric, time, os from _tdmt import * scene = poser.Scene() #leftright = 0 # 0 = +x --> -x; 1 = -x --> +x def run(): thelist = ["Match Sym","Closest Sym"] which = poser.DialogSimple.AskMenu("Sym Type:","Pick Method",thelist) if not which: match_sym() else: if which == "Closest Sym": closest_sym() if which == "Match Sym": match_sym() def match_sym(): #print "match" thelist = ["+x to -x", "-x to +x"] side = poser.DialogSimple.AskMenu("Pick side","To transfer",thelist) if side: leftright = thelist.index(side) act = scene.CurrentActor() geom = act.Geometry() shrinkMesh = myMesh(geom,"vert",act) shrinkMesh.verts = shrinkMesh.mesh.GetWorldVertices() shrinkMesh.baseverts = shrinkMesh.mesh.GetVertices() shrinkMesh.points = shrinkMesh.mesh.GetWorldVertices() morphsD = {} # First, get the worldspace verts without morph and magnet deformations. for parm in act.Parameters(): if parm.IsMorphTarget() or parm.TypeCode() == poser.kParmCodeDEFORMERPROP: morphsD[parm.Name()] = parm.Value() parm.SetValue(0.0) shrinkMesh.mesh.VertPositionsChanged() shrinkMesh.verts2 = shrinkMesh.mesh.GetWorldVertices() for parmname in morphsD.keys(): # Restore the morph and deformer settings parm = act.Parameter(parmname) parm.SetValue(morphsD[parmname]) morphsD = {} shrinkMesh.mesh.VertPositionsChanged() symdir = {} for vi in range(shrinkMesh.mesh.NumVertices()): v = shrinkMesh.baseverts[vi] tup = (v[0],v[1],v[2]) symdir[tup] = vi for vi in range(shrinkMesh.mesh.NumVertices()): v = shrinkMesh.baseverts[vi] if (leftright and v[0] < 0.0) or (not leftright and v[0] > 0.0): tup = (-v[0],v[1],v[2]) if symdir.has_key(tup): symvi = symdir[tup] else: if not Vector(v[0],v[0],v[0]): # Flatten central verts along x if they have 0.0 x in baseverts v1 = shrinkMesh.verts[vi] shrinkMesh.points[vi] = Vector(0.0,v1[1],v1[2]) continue v2 = shrinkMesh.verts[vi] - shrinkMesh.verts2[vi] # Delta for deformed worldspace to undeformed worldspace v2 = Vector(-v2[0],v2[1],v2[2]) + shrinkMesh.verts2[symvi] # Flip the delta across x and add to undeformed worldspace symvert shrinkMesh.points[symvi] = v2.Clone() setShape(0,shrinkMesh,act,addDeltas=0) scene.DrawAll() def closest_sym(): #print "closest" import time stime = time.time() usebox = 0 print_missed = 0 boxes = 0 delboxes = 0 thelist = ["+x to -x", "-x to +x"] side = poser.DialogSimple.AskMenu("Pick side","To transfer",thelist) if side: leftright = thelist.index(side) act = scene.CurrentActor() if delboxes: dlist = [i.Name() for i in scene.Actors() if (i.IsProp() and i.Name().find("_") != -1)] if dlist: delete_test_boxes(dlist) scene.SelectActor(act) act = scene.CurrentActor() geom = act.Geometry() shrinkMesh = myMesh(geom,"vert",act) shrinkMesh.verts = shrinkMesh.mesh.GetWorldVertices() shrinkMesh.baseverts = shrinkMesh.mesh.GetVertices() shrinkMesh.points = shrinkMesh.mesh.GetWorldVertices() log = [-1 for i in shrinkMesh.verts] for vert in range(shrinkMesh.mesh.NumVertices()): v = shrinkMesh.verts[vert] if (v[0] > 0.0 and leftright == 0) or (v[0] < 0.0 and leftright == 1): closest = -1 close = 100000 v1c = Vector(-v[0],v[1],v[2]) if usebox: sz = 0.001 bbox = [v1c[0]-sz, v1c[0]+sz, v1c[1]-sz, v1c[1]+sz, v1c[2]-sz, v1c[2]+sz] screen = in_box(bbox,shrinkMesh.verts) for vi in range(shrinkMesh.mesh.NumVertices()): v2c = shrinkMesh.verts[vi] if (v2c[0] < 0.0 and leftright == 0) or (v2c[0] > 0.0 and leftright == 1): test = v2c - v1c if not test: # Exit early if there's a perfect match closest = vi break if (v2c[2] >= 0.0 and v1c[2] >= 0.0) or (v2c[2] <= 0.0 and v1c[2] <= 0.0): if (v2c[1] >= 0.0 and v1c[1] >= 0.0) or (v2c[1] <= 0.0 and v1c[1] <= 0.0): if (usebox and screen[vi]) or (not usebox): d = v1c.PointDistance(v2c) if d < close: closest = vi close = d log[closest] = v1c for vert in range(geom.NumVertices()): if log[vert] != -1: v1c = log[vert] shrinkMesh.points[vert] = v1c.Clone() setShape(0,shrinkMesh,act,addDeltas=0) if print_missed or boxes: non = [i for i in range(geom.NumVertices()) if ((shrinkMesh.verts[i][0] < 0.0 and leftright == 0) or (shrinkMesh.verts[i][0] > 0.0 and leftright == 1))and log[i] == -1] if print_missed: print time.time() - stime print geom.NumVertices() print log.count(-1) print "Unmatched verts:" print non if boxes: for i in non: #testing_boxes(shrinkMesh.verts[i],0.0005,"_%s"%(i),0,0) # unmerged - dangerous testing_boxes(shrinkMesh.verts[i],0.0005,"_",0,1) ###========= AABB Stuff ====================================================== def in_box(bbox,verts): """Simple bounding box check""" inbox = [0 for i in range(len(verts))] for vi in range(len(verts)): vert = verts[vi] if vert.x > bbox[0] and vert.x < bbox[1]: if vert.y > bbox[2] and vert.y < bbox[3]: if vert.z > bbox[4] and vert.z < bbox[5]: inbox[vi] = 1 return inbox class Bbox: """ class to store bounding box data """ def __init__(self,vertpos1,increase): self.box1 = b_box_ock(vertpos1) self.box1 = increase_box(self.box1,increase) self.center1 = box_center(self.box1) def mixcenters(self,c1,c2): self.mcenter = c1 + c2 for i in range(3): self.mcenter[i] /= 2 def b_box_ock(vertpos): """ create bounding boxes """ minX = 1000000.0; maxX = -1000000.0 minY = 1000000.0; maxY = -1000000.0 minZ = 1000000.0; maxZ = -1000000.0 for i in range(len(vertpos)): tx = vertpos[i][0] ty = vertpos[i][1] tz = vertpos[i][2] if tx < minX: minX = tx if tx > maxX: maxX = tx if ty < minY: minY = ty if ty > maxY: maxY = ty if tz < minZ: minZ = tz if tz > maxZ: maxZ = tz return [minX,maxX,minY,maxY,minZ,maxZ] def mix_boxes(b1,b2,box,increase): """ merge two bounding boxes """ g = 1000000.0 box = [g,-g,g,-g,g,-g] for i in range(6): if (i+1)%2 != 0: # Odd is min if b1[i] < box[i]: box[i] = b1[i] elif (i+1)%2 == 0: if b1[i] > box[i]: box[i] = b1[i] for i in range(6): if (i+1)%2 != 0: # Odd is min if b2[i] < box[i]: box[i] = b2[i] elif (i+1)%2 == 0: if b2[i] > box[i]: box[i] = b2[i] box = increase_box(box,increase) return box def increase_box(box,increase): """ increase the bounding box size """ for i in range(6): if i%1 == 0: increase = -increase box[i] = box[i] + increase return box def box_center(box): """ find the center of the box To use results as a cylinder: use box_center x,z with vertex y Game gems: can also do (min + max)/2 for each axis. """ minX,maxX,minY,maxY,minZ,maxZ = box cx = (abs(minX-maxX)/2)+minX cy = (abs(minY-maxY)/2)+minY cz = (abs(minZ-maxZ)/2)+minZ return Vector(cx,cy,cz) ###============ Testing box stuff ============================================== def deleteboxes(): """ Super-slow when there are too many boxes.... """ dlist = [i.Name() for i in scene.Actors() if (i.IsProp() and i.Name().find("_") != -1)] if dlist: delete_test_boxes(dlist) def testing_boxes(point,sz,name,disp,merge): """ 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) def delete_test_boxes(deletelist): """ Delete testing props """ for ai in range(len(deletelist)-1,-1,-1): a = deletelist[ai] scene.SelectActor(scene.Actor(a)) scene.DeleteCurrentProp() deletelist.pop(ai) scene.DrawAll() scene.ProcessSomeEvents() def build_box(b1,name,display,merge,vertarray=None): """ 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) scene.DrawAll() ##=============Shaping functions================================================ def setShape(bake,mesh,shrinkObj,addDeltas=1,do_print=0): # Set the morphs or bake the shape """Bake the mesh or create the morphs""" if not bake: if do_print: print "Creating the new morph..." morphs = {} if addDeltas: deltas = [Vector() for i in mesh.verts] for parm in shrinkObj.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("symmetry",shrinkObj,"") shrinkObj.SpawnTarget(mtname) newMT = shrinkObj.Parameter(mtname) newMT.SetValue(1.0) for parmname in morphs.keys(): parm = shrinkObj.Parameter(parmname) parm.SetValue(morphs[parmname]) else: if do_print: print "Baking the geometry..." for vi in range(len(mesh.points)): if bake: vert = shrinkObj.Geometry().Vertex(vi) vert.SetX(mesh.points[vi][0]) vert.SetY(mesh.points[vi][1]) vert.SetZ(mesh.points[vi][2]) else: 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) if bake: shrinkObj.MarkGeomChanged() 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 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 #=============================================================================================== # class myMesh() #=============================================================================================== class myMesh(object): """Initialize the mesh data""" def __init__(self,geom,meshtype,act): if meshtype == "vert": self.mesh = Mesh(geom) # pyd Mesh instance embedded self.name = act.Name() fig = act.ItsFigure() if fig == None: self.fig = "None" else: self.fig = fig.Name() if meshtype == "poly": self.mesh = Mesh(geom) run()