import poser import Numeric, time, os from _tdmt import * scene = poser.Scene() do_print = 0 def run(): avg = 0 bake = 0 addDeltas = 0 reps = poser.DialogSimple.AskInt("Number of repititions (default=4)") if not reps: reps = 4 mix = poser.DialogSimple.AskFloat("Intensity (e.g. 0.5)") if not mix: mix = 0.0 shrinkObj = scene.CurrentActor() shrinkMesh = myMesh(shrinkObj.Geometry(),"vert",shrinkObj) shrinkMesh.verts = shrinkMesh.mesh.GetWorldVertices() shrinkMesh.verts1 = shrinkMesh.mesh.GetVertices() shrinkMesh.vnorms = shrinkMesh.mesh.GetWorldNormals() shrinkMesh.baseverts = shrinkMesh.mesh.GetVertices() shrinkMesh.ngons = psrPolygonList(shrinkObj.Geometry()) shrinkMesh.vpolys = VertexPolys(shrinkMesh.ngons,shrinkMesh.mesh.NumVertices()) shrinkMesh.mesh.StructureChanged() # To clear .pyd internal copies of the above lists shrinkMesh.points = shrinkMesh.mesh.GetWorldVertices() # Store hit locations to pass to post-processing functions shrinkMesh.matchlist = [1 for i in shrinkMesh.verts] find_detail(shrinkMesh,avg) restore_detail(shrinkMesh,reps=reps,mix=mix) #fix_overlap2(shrinkMesh) setShape(bake,shrinkMesh,shrinkObj,addDeltas=addDeltas) 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.InternalName() # used for tracking and setting up the morph self.inc_screen = None # screening box list if meshtype == "poly": self.mesh = Mesh(geom) #self.name = act.InternalName() #Need a vneighbors routine for these. def find_detail(mesh,avg): """ 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. """ if do_print: print "Finding mesh relationships for detail..." if not avg: # vneighbors may already have been created for average_normals() mesh.vneighbors = mesh.mesh.GetVertNeighborsByNgons() mesh.detail = [Vector() for i in mesh.verts1] for vi in range(len(mesh.verts1)): nbs = mesh.vneighbors[vi] vert = mesh.verts1[vi] avg = Vector() for i in range(len(nbs)): nvi = nbs[i] nv = mesh.verts1[nvi] for i in range(3): avg[i] += nv[i] for i in range(3): avg[i] /= len(nbs) mesh.detail[vi] = vert - avg def restore_detail(mesh,reps=4,screen=0,screen2=0,deviate=0,threshold=0.0001,dircheck=0,mix=0.0,normtype=1,preset=0,refresh=0): if preset == 1: reps,screen,screen2,deviate,threshold,dircheck,mix,normtype = (4,0,0,0,0.000001,1,0.0,2) if preset == 2: reps,screen,screen2,deviate,threshold,dircheck,mix,normtype = (4,0,0,1,0.000001,1,0.0,0) if preset == 3: reps,screen,screen2,deviate,threshold,dircheck,mix,normtype = (4,0,0,1,0.000001,1,0.6,2) if preset == 4: reps,screen,screen2,deviate,threshold,dircheck,mix,normtype = (4,0,0,0,0.000001,1,0.0,0) if preset == 5: reps,screen,screen2,deviate,threshold,dircheck,mix,normtype = (4,0,0,0,0.000001,1,0.6,2) """ Works with find_detail to try to restore basic positions of neighbor vertices, relative to one another. This reduces mesh stretching and can pull uncorrelated vertices into place, but it can also move vertices away from proper matched surfaces. To compensate, this applies various screening techniques to restrict which verts are moved, or how, or how much. Multiple passes may be required to pull unmatched verts fully into line. Because this works with vertex neighbors, clusters of unmatched vertices won't respond at their center with only one pass. Each pass basically moves inward one "row" of vertices in such unmatched clusters. Enough passes should be run to reach the center of such a group. Kind of a kludge, really.... """ #reps = number of looped repititions for function #screen = favor verts without matches 0=process all, 1=process only unmatched verts #screen2 = if 1, this sets screen from 0 to 1 at the end of the first reps loop (clean up unmatched more than matched) #deviate = screen out verts which do not deviate from relative find_detail beyond a certain threshold #threshold = threshold value for deviate #dircheck = if alterations in this function may be moving the vert back into the collided mesh, compensate #mix = lessen the effect of this function by mixing results in with inherited points #normtype = normals to use for dircheck compensation 0=vertex normals of mesh, 1=away from collision mesh, 2=half and half for norms in 1 and 2 #preset = use a defined preset for the function (as shown above) if do_print: print "Restoring mesh relationships for detail..." if refresh == 1: # New vertex normals from unaltered base mesh positions mesh.vnorms = mesh.mesh.GetWorldNormals() if refresh == 2: # New vertex normals from points list positions pnorms = PolyFaceNormals(mesh.ngons,mesh.points) mesh.vnorms = PolyVertexNormals(pnorms,mesh.vpolys) detail = mesh.detail if screen: if do_print: print "Trying to restore only unmatched verts..." for rep in range(reps): points = mesh.points # This seems to need to go here to refresh the references... why? if do_print: print "Restoration pass %s of %s..." %(rep+1,reps) for vi in range(len(mesh.verts1)): if ((screen == 1) and (mesh.matchlist[vi] == -1)) or screen == 0: vert = mesh.verts1[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) screenlist = [1,1,1] if deviate: # Check deviation from neighbor average against threshold if mesh.matchlist[vi] != -1: sub1 = vert - detail[vi] sub2 = points[vi] - avg dev = sub1 - sub2 #dev = avg - detail[vi] for i in range(3): if abs(dev[i]) <= threshold:# Backwards? screenlist[i] = 0 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] if dircheck: # Reposition verts if they are (hypothetically) moving back toward the colliding poly due to the above if mesh.matchlist[vi] != -1: if newpoint != points[vi]: norm = newpoint - points[vi] check = norm ^ mesh.vnorms[vi] if check < 0.0: # We've moved toward the colliding poly (hypothetically) vlen = norm.Length() norm = ~norm if normtype == 0: newpoint = newpoint.Project(mesh.vnorms[vi],vlen) if normtype == 1: newpoint = newpoint.Project(-norm,vlen) if normtype == 2: np1 = newpoint.Project(-norm,vlen) np2 = newpoint.Project(mesh.vnorms[vi],vlen) newpoint = np1 + np2 newpoint = newpoint.Scale(0.5) if normtype == 3: newpoint = points[vi].Clone() if mix: # Mix in the corrections delta = newpoint - points[vi] newpoint = points[vi].Project(delta,mix) mesh.points[vi] = newpoint.Clone() if screen2: screen = 1 # Restrict passes >= 1 to unmatched verts def fix_overlap2(mesh): points = [i.Clone() for i in mesh.points] for vi in range(len(mesh.points)): point = mesh.points[vi].Clone() nbs = mesh.vneighbors[vi] vnorms = mesh.mesh.GetWorldNormals() for ni in range(len(nbs)): nvi = nbs[ni] nv = mesh.points[nvi].Clone() basedelta = mesh.verts[vi] - mesh.verts[nvi] newdelta = point - nv vdot = newdelta ^ basedelta if vdot < 0.0: length = basedelta.Length() # v|| = n*(v ^ n / ||n||**2) --> project vector v onto vector n #basedelta = ~basedelta #movedelta = basedelta*(newdelta^basedelta) #point = nv.Project(movedelta,length) #point = nv.Project(basedelta,length) # This should be the same as the above point = nv.Project(-newdelta,length) points[vi].Copy(point)# = point.Clone() mesh.points = [i.Clone() for i in points] def setShape(bake,mesh,shrinkObj,addDeltas=1): # 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("restore_detail",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 run()