def regenerate(self): if not self.obj: return mesh = self.obj.getData(mesh=True) panelimage = mesh.faces[0].image panelimage.getSize() # force load Window.WaitCursor(1) Window.DrawProgressBar(0, 'Panel regions') for n in range(1, PanelRegionHandler.REGIONCOUNT + 1): Window.DrawProgressBar(n / 6.0, 'Panel regions') img = mesh.faces[n].image if img != panelimage: (width, height) = img.size xoff = self.obj.getProperty('x%d' % n).data yoff = self.obj.getProperty('y%d' % n).data for y in range(height): for x in range(width): rgba = panelimage.getPixelI(xoff + x, yoff + y) if not rgba[3]: img.setPixelI( x, y, (102, 102, 255, 255)) # hilite transparent else: img.setPixelI(x, y, rgba[:3] + [255]) img.glFree() # force reload img.pack() # repack mesh.update() Window.RedrawAll(0) Window.DrawProgressBar(1, 'Finished') Window.WaitCursor(0)
def file_callback(filename): obj = NETimport(filename) try: obj.doimport() Blender.Scene.GetCurrent().update() except ParseError, e: Window.WaitCursor(0) Window.DrawProgressBar(0, 'ERROR') if e.type == ParseError.HEADER: msg = 'This is not a valid X-Plane v6, v7 or v8 OBJ file' elif e.type == ParseError.NAME: msg = 'Missing dataref or light name at line %s\n' % obj.m.lineno elif e.type == ParseError.MISC: msg = '%s at line %s' % (e.value, obj.m.lineno) else: thing = ParseError.TEXT[e.type] if e.value: msg = 'Expecting a %s, found "%s" at line %s' % ( thing, e.value, obj.m.lineno) else: msg = 'Missing %s at line %s' % (thing, obj.m.lineno) print "ERROR:\t%s\n" % msg Draw.PupMenu("ERROR%%t|%s" % msg) Window.RedrawAll() Window.DrawProgressBar(1, 'ERROR')
def export(self, scene): theObjects = scene.objects print "Starting OBJ export to " + self.filename if not checkFile(self.filename): return Blender.Window.WaitCursor(1) Window.DrawProgressBar(0, "Examining textures") self.texture = getTexture(self, theObjects, self.iscsl, 7) if self.regions: (pwidth, pheight) = PanelRegionHandler().panelimage().size for img, (n, x1, y1, width, height) in self.regions.iteritems(): self.regions[img] = (float(x1) / pwidth, float(y1) / pheight, float(width) / pwidth, float(height) / pheight) #clock=time.clock() # Processor time self.file = open(self.filename, "w") self.writeHeader() self.writeObjects(theObjects) checkLayers(self, theObjects) self.file.close() Window.DrawProgressBar(1, "Finished") #print "%s CPU time" % (time.clock()-clock) print "Finished - exported %s primitives\n" % self.nprim if self.log: r = Draw.PupMenu(("Exported %s primitives%%t|" % self.nprim) + '|'.join([a[0] for a in self.log])) if r > 0: raise ExportError(None, self.log[r - 1][1]) else: Draw.PupMenu("Exported %s primitives%%t|OK" % self.nprim)
def file_callback (filename): print "Starting DSF import from " + filename Window.WaitCursor(1) if 1:#XXXtry: xppath=normpath(join(dirname(filename),pardir,pardir,pardir,pardir)) dirs=[i.lower() for i in listdir(xppath)] if 'default scenery' in dirs and 'custom scenery' not in dirs: xppath=normpath(join(xppath,pardir)) # Process libraries Window.DrawProgressBar(0, "Scanning libraries") for d in listdir(xppath): if d.lower()=='custom scenery': readLIBs(join(xppath,d), libterrain) break for d in listdir(xppath): if d.lower()=='resources': for d2 in listdir(join(xppath,d)): if d2.lower()=='default scenery': readLIBs(join(xppath,d,d2), libterrain) break break Window.DrawProgressBar(0, "Importing") readDSF(filename) elif 0:#except IOError, e: Window.WaitCursor(0) Window.DrawProgressBar(1, "ERROR") print("ERROR:\t%s.\n" % e.strerror) Draw.PupMenu("ERROR: %s" % e.strerror) return elif 0:#except: Window.WaitCursor(0) Window.DrawProgressBar(1, "ERROR") print("ERROR:\tCan't read DSF.\n") Draw.PupMenu("ERROR: Can't read DSF") return else: Window.WaitCursor(0) Window.DrawProgressBar(1, "Finished") Window.RedrawAll() print "Finished\n"
def copy_morphs(_from, _to, PREF_SEL_ONLY, PREF_NO_XCROSS): ob_from, me_from, world_verts_from, from_morph_key = _from ob_to, me_to, world_verts_to, dummy = _to del dummy # insert base key at frame 1, using relative keys me_to.insertKey(1, 'relative') # list of snap indices snap_indices = [ get_snap_idx(co, world_verts_from, PREF_NO_XCROSS) for idx, co in world_verts_to ] # create keys for i, from_key_block in enumerate(me_from.key.blocks[1:]): # report progress Window.DrawProgressBar( 0.01 + 0.98 * (i / float(len(me_from.key.blocks) - 1)), 'Copy "%s" -> "%s" ' % (ob_from.name, ob_to.name)) # get deformation morph = [ vec_new - vec_old for (vec_new, vec_old ) in zip(from_key_block.data, me_from.key.blocks[0].data) ] # deform me_to for me_to_vert, me_to_vert_base, snap_idx in zip( me_to.vertices, me_to.key.blocks[0].data, snap_indices): me_to_vert.co[0] = me_to_vert_base[0] + morph[snap_idx][0] me_to_vert.co[1] = me_to_vert_base[1] + morph[snap_idx][1] me_to_vert.co[2] = me_to_vert_base[2] + morph[snap_idx][2] # insert shape key me_to.insertKey(1, 'relative') me_to.key.blocks[-1].name = from_key_block.name # reset mesh to base morph for me_to_vert, me_to_vert_base in zip(me_to.vertices, me_to.key.blocks[0].data): me_to_vert.co[0] = me_to_vert_base[0] me_to_vert.co[1] = me_to_vert_base[1] me_to_vert.co[2] = me_to_vert_base[2] # copy ipo if there is one if me_from.key.ipo: me_to.key.ipo = me_from.key.ipo me_to.update()
def doScript(): # Main script Function # Consists of choosing between 2 loops, one with a redraw, one without, see comments for why global CUROFFS translateParams() total = len(PARAMS['ImagePaths']) broken = 0 if GUIPARAMS['RedrawImp'].val: # Reduces the need to compare on every go through the loop for i, path in enumerate(PARAMS['ImagePaths']): CUROFFS = i # Could be passed to the import Function, but I chose a global instead Window.DrawProgressBar(float(i)/total, "Importing %i of %i Images..." %(i+1, total)) imgImport(path) Blender.Redraw() if Blender.Get('version') >= 246: if Window.TestBreak(): broken = 1 break else: for i, path in enumerate(PARAMS['ImagePaths']): CUROFFS = i Window.DrawProgressBar(float(i)/total, "Importing %i of %i Images..." %(i+1, total)) imgImport(path) if Blender.Get('version') >= 246: if Window.TestBreak(): broken = 1 break if broken: Window.DrawProgressBar(1.0, "Script Execution Aborted") else: Window.DrawProgressBar(1.0, "Finished Importing") Blender.Redraw() # Force a refresh, since the user may have chosen to not refresh as they go along return
def main(): scn = bpy.data.scenes.active ob = scn.objects.active if not ob or ob.type != 'Mesh': return is_editmode = Window.EditMode() if is_editmode: Window.EditMode(0) mousedown_wait() # so the menu items clicking dosnt trigger the mouseclick Window.DrawProgressBar(0.0, '') Window.DrawProgressBar(0.1, '(1 of 3) Click on a face corner') # wait for a click mouse_buttons = Window.GetMouseButtons() while not mouse_buttons & LMB: sys.sleep(10) mouse_buttons = Window.GetMouseButtons() # Allow for RMB cancel if mouse_buttons & RMB: return while mouse_buttons & LMB: sys.sleep(10) mouse_buttons = Window.GetMouseButtons() Window.DrawProgressBar(0.2, '(2 of 3 ) Click confirms the U coords') mousedown_wait() obmat = ob.matrixWorld screen_x, screen_y = Window.GetMouseCoords() mouseInView, OriginA, DirectionA = mouseViewRay(screen_x, screen_y, obmat) if not mouseInView or not OriginA: return me = ob.getData(mesh=1) # Get the face under the mouse face_click, isect, side = BPyMesh.pickMeshRayFace(me, OriginA, DirectionA) if not face_click: return proj_z_component = face_click.no if not face_click: return # Find the face vertex thats closest to the mouse, # this vert will be used as the corner to map from. best_v = None best_length = 10000000 vi1 = None for i, v in enumerate(face_click.v): l = (v.co - isect).length if l < best_length: best_v = v best_length = l vi1 = i # now get the 2 edges in the face that connect to v # we can work it out fairly easerly if len(face_click) == 4: if vi1 == 0: vi2, vi3 = 3, 1 elif vi1 == 1: vi2, vi3 = 0, 2 elif vi1 == 2: vi2, vi3 = 1, 3 elif vi1 == 3: vi2, vi3 = 2, 0 else: if vi1 == 0: vi2, vi3 = 2, 1 elif vi1 == 1: vi2, vi3 = 0, 2 elif vi1 == 2: vi2, vi3 = 1, 0 face_corner_main = face_click.v[vi1].co face_corner_a = face_click.v[vi2].co face_corner_b = face_click.v[vi3].co line_a_len = (face_corner_a - face_corner_main).length line_b_len = (face_corner_b - face_corner_main).length orig_cursor = Window.GetCursorPos() Window.SetCursorPos(face_corner_main.x, face_corner_main.y, face_corner_main.z) SHIFT = Window.Qual.SHIFT MODE = 0 # firstclick, 1, secondclick mouse_buttons = Window.GetMouseButtons() project_mat = Matrix([0, 0, 0], [0, 0, 0], [0, 0, 0]) def get_face_coords(f): f_uv = f.uv return [(v.co - face_corner_main, f_uv[i]) for i, v in enumerate(f.v)] if me.faceUV == False: me.faceUV = True coords = [(co, uv) for f in me.faces if f.sel for co, uv in get_face_coords(f)] coords_orig = [uv.copy() for co, uv in coords] USE_MODIFIER = using_modifier(ob) while 1: if mouse_buttons & LMB: if MODE == 0: mousedown_wait() Window.DrawProgressBar( 0.8, '(3 of 3 ) Click confirms the V coords') MODE = 1 # second click # Se we cont continually set the length and get float error proj_y_component_orig = proj_y_component.copy() else: break elif mouse_buttons & RMB: # Restore old uvs for i, uv_orig in enumerate(coords_orig): coords[i][1][:] = uv_orig break mouse_buttons = Window.GetMouseButtons() screen_x, screen_y = Window.GetMouseCoords() mouseInView, OriginA, DirectionA = mouseViewRay( screen_x, screen_y, obmat) if not mouseInView: continue # Do a ray tri intersection, not clipped by the tri new_isect = Intersect(face_corner_main, face_corner_a, face_corner_b, DirectionA, OriginA, False) new_isect_alt = new_isect + DirectionA * 0.0001 # The distance from the mouse cursor ray vector to the edge line_isect_a_pair = LineIntersect(new_isect, new_isect_alt, face_corner_main, face_corner_a) line_isect_b_pair = LineIntersect(new_isect, new_isect_alt, face_corner_main, face_corner_b) # SHIFT to flip the axis. is_shift = Window.GetKeyQualifiers() & SHIFT if MODE == 0: line_dist_a = (line_isect_a_pair[0] - line_isect_a_pair[1]).length line_dist_b = (line_isect_b_pair[0] - line_isect_b_pair[1]).length if line_dist_a < line_dist_b: proj_x_component = face_corner_a - face_corner_main y_axis_length = line_b_len x_axis_length = (line_isect_a_pair[1] - face_corner_main).length else: proj_x_component = face_corner_b - face_corner_main y_axis_length = line_a_len x_axis_length = (line_isect_b_pair[1] - face_corner_main).length proj_y_component = proj_x_component.cross(proj_z_component) proj_y_component.length = 1 / y_axis_length proj_x_component.length = 1 / x_axis_length if is_shift: proj_x_component.negate() else: proj_y_component[:] = proj_y_component_orig if line_dist_a < line_dist_b: proj_y_component.length = 1 / (line_isect_a_pair[1] - new_isect).length else: proj_y_component.length = 1 / (line_isect_b_pair[1] - new_isect).length if is_shift: proj_y_component.negate() # Use the existing matrix to make a new 3x3 projecton matrix project_mat[0][:] = -proj_y_component project_mat[1][:] = -proj_x_component project_mat[2][:] = proj_z_component # Apply the projection matrix for proj_co, uv in coords: uv[:] = (project_mat * proj_co)[0:2] if USE_MODIFIER: me.update() Window.Redraw(Window.Types.VIEW3D) Window.SetCursorPos(*orig_cursor) if is_editmode: Window.EditMode(1) Window.RedrawAll()
else: proj_y_component.length = 1 / (line_isect_b_pair[1] - new_isect).length if is_shift: proj_y_component.negate() # Use the existing matrix to make a new 3x3 projecton matrix project_mat[0][:] = -proj_y_component project_mat[1][:] = -proj_x_component project_mat[2][:] = proj_z_component # Apply the projection matrix for proj_co, uv in coords: uv[:] = (project_mat * proj_co)[0:2] if USE_MODIFIER: me.update() Window.Redraw(Window.Types.VIEW3D) Window.SetCursorPos(*orig_cursor) if is_editmode: Window.EditMode(1) Window.RedrawAll() if __name__ == '__main__': main() Window.DrawProgressBar(1.0, '')
def vertexGradientPick(ob, MODE): #MODE 0 == VWEIGHT, 1 == VCOL me = ob.getData(mesh=1) if not me.faceUV: me.faceUV = True Window.DrawProgressBar(0.0, '') mousedown_wait() if MODE == 0: act_group = me.activeGroup if act_group == None: mousedown_wait() Draw.PupMenu('Error, mesh has no active group.') return # Loop until click Window.DrawProgressBar(0.25, 'Click to set gradient start') mouseup() obmat = ob.matrixWorld screen_x, screen_y = Window.GetMouseCoords() mouseInView, OriginA, DirectionA = mouseViewRay(screen_x, screen_y, obmat) if not mouseInView or not OriginA: return # get the mouse weight if MODE == 0: pickValA = BPyMesh.pickMeshGroupWeight(me, act_group, OriginA, DirectionA) if MODE == 1: pickValA = BPyMesh.pickMeshGroupVCol(me, OriginA, DirectionA) Window.DrawProgressBar(0.75, 'Click to set gradient end') mouseup() TOALPHA = Window.GetKeyQualifiers() & Window.Qual.SHIFT screen_x, screen_y = Window.GetMouseCoords() mouseInView, OriginB, DirectionB = mouseViewRay(screen_x, screen_y, obmat) if not mouseInView or not OriginB: return if not TOALPHA: # Only get a second opaque value if we are not blending to alpha if MODE == 0: pickValB = BPyMesh.pickMeshGroupWeight(me, act_group, OriginB, DirectionB) else: pickValB = BPyMesh.pickMeshGroupVCol(me, OriginB, DirectionB) else: if MODE == 0: pickValB = 0.0 else: pickValB = [0.0, 0.0, 0.0] # Dummy value # Neither points touched a face if pickValA == pickValB == None: return # clicking on 1 non face is fine. just set the weight to 0.0 if pickValA == None: pickValA = 0.0 # swap A/B OriginA, OriginB = OriginB, OriginA DirectionA, DirectionB = DirectionB, DirectionA pickValA, pickValB = pickValA, pickValB TOALPHA = True if pickValB == None: pickValB = 0.0 TOALPHA = True # set up 2 lines so we can measure their distances and calc the gradient # make a line 90d to the grad in screenspace. if (OriginA - OriginB ).length <= eps: # Persp view. same origin different direction cross_grad = DirectionA.cross(DirectionB) ORTHO = False else: # Ortho - Same direction, different origin cross_grad = DirectionA.cross(OriginA - OriginB) ORTHO = True cross_grad.normalize() cross_grad = cross_grad * 100 lineA = (OriginA, OriginA + (DirectionA * 100)) lineB = (OriginB, OriginB + (DirectionB * 100)) if not ORTHO: line_angle = AngleBetweenVecs(lineA[1], lineB[1]) / 2 line_mid = (lineA[1] + lineB[1]) * 0.5 VSEL = [False] * (len(me.verts)) # Get the selected faces and apply the selection to the verts. for f in me.faces: if f.sel: for v in f.v: VSEL[v.index] = True groupNames, vWeightDict = BPyMesh.meshWeight2Dict(me) def grad_weight_from_co(v): ''' Takes a vert and retuens its gradient radio between A and B ''' if not VSEL[v.index]: # Not bart of a selected face? return None, None v_co = v.co # make a line 90d to the 2 lines the user clicked. vert_line = (v_co - cross_grad, v_co + cross_grad) xA = LineIntersect(vert_line[0], vert_line[1], lineA[0], lineA[1]) xB = LineIntersect(vert_line[0], vert_line[1], lineB[0], lineB[1]) if not xA or not xB: # Should never happen but support it anyhow return None, None wA = (xA[0] - xA[1]).length wB = (xB[0] - xB[1]).length wTot = wA + wB if not wTot: # lines are on the same point. return None, None ''' Get the length of the line between both intersections on the 2x view lines. if the dist between lineA+VertLine and lineB+VertLine is greater then the lenth between lineA and lineB intersection points, it means that the verts are not inbetween the 2 lines. ''' lineAB_length = (xA[1] - xB[1]).length # normalzie wA = wA / wTot wB = wB / wTot if ORTHO: # Con only use line length method with parelelle lines if wTot > lineAB_length + eps: # vert is outside the range on 1 side. see what side of the grad if wA > wB: wA, wB = 1.0, 0.0 else: wA, wB = 0.0, 1.0 else: # PERSP, lineA[0] is the same origin as lineB[0] # Either xA[0] or xB[0] can be used instead of a possible x_mid between the 2 # as long as the point is inbetween lineA and lineB it dosent matter. a = AngleBetweenVecs(lineA[0] - xA[0], line_mid) if a > line_angle: # vert is outside the range on 1 side. see what side of the grad if wA > wB: wA, wB = 1.0, 0.0 else: wA, wB = 0.0, 1.0 return wA, wB grad_weights = [grad_weight_from_co(v) for v in me.verts] if MODE == 0: for v in me.verts: i = v.index if VSEL[i]: wA, wB = grad_weights[i] if wA != None: # and wB if TOALPHA: # Do alpha by using the exiting weight for try: pickValB = vWeightDict[i][act_group] except: pickValB = 0.0 # The weights not there? assume zero # Mix2 2 opaque weights vWeightDict[i][act_group] = pickValB * wA + pickValA * wB else: # MODE==1 VCol for f in me.faces: if f.sel: f_v = f.v for i in xrange(len(f_v)): v = f_v[i] wA, wB = grad_weights[v.index] c = f.col[i] if TOALPHA: pickValB = c.r, c.g, c.b c.r = int(pickValB[0] * wA + pickValA[0] * wB) c.g = int(pickValB[1] * wA + pickValA[1] * wB) c.b = int(pickValB[2] * wA + pickValA[2] * wB) # Copy weights back to the mesh. BPyMesh.dict2MeshWeight(me, groupNames, vWeightDict) Window.DrawProgressBar(1.0, '')
layers = [] if isinstance(e.objs, tuple): (o, mesh, faces) = e.objs o.select(1) layers = o.layers for f in mesh.faces: f.sel = 0 if faces: for f in faces: f.sel = 1 for i in range(len(mesh.faces)): if mesh.faces[i] == faces[0]: mesh.activeFace = i break else: for o in e.objs: o.select(1) for layer in o.layers: if (layer <= 3 or not o.Layers & 7) and not layer in layers: layers.append(layer) Window.ViewLayers(layers) Window.RedrawAll() if e.msg: Window.WaitCursor(0) Window.DrawProgressBar(0, 'ERROR') print "ERROR:\t%s.\n" % e.msg Draw.PupMenu("ERROR%%t|%s" % e.msg) Window.DrawProgressBar(1, 'ERROR') if obj and obj.file: obj.file.close()
def writeObjects(self, theObjects): if self.layermask == 1: lseq = [1] else: lseq = [1, 2, 4] h = PanelRegionHandler() # Count the objects nobj = 0 objlen = 1 for layer in lseq: for object in theObjects: if object.Layer & layer: objlen = objlen + 1 if self.optimise: npasses = Face.BUCKET + 1 else: npasses = 1 objlen = objlen * (2 + npasses) for layer in lseq: meshes = [] self.updateLayer(layer) # 1st pass: Build meshes for o in range(len(theObjects) - 1, -1, -1): object = theObjects[o] if not object.Layer & layer or h.isHandlerObj(object): continue Window.DrawProgressBar( float(nobj) / objlen, "Exporting %s%% ..." % (nobj * 100 / objlen)) nobj = nobj + 1 objType = object.getType() if objType == "Mesh": if isLight(object): print 'Warn:\tIgnoring custom light "%s"' % object.name self.log.append( ('Ignoring custom light "%s"' % object.name, [object])) elif not isLine(object, self.linewidth): meshes.append(self.sortMesh(object, layer)) elif objType == "Lamp": pass # Handled later elif objType == 'Empty': for prop in object.getAllProperties(): if prop.type in ['INT', 'FLOAT' ] and prop.name.startswith('group '): self.file.write( "\nATTR_layer_group\t%s\t%d\t//\n\n" % (prop.name[6:].strip(), int(prop.data))) #elif objType not in ['Camera','Lattice']: # print 'Warn:\tIgnoring %s "%s"' % (objType.lower(), object.name) # self.log.append(('Ignoring %s "%s"' % (objType.lower(), object.name), [object])) # Hack! Find a kosher panel texture and put it last if self.iscockpit: panelsorted = 0 i = len(meshes) - 1 while i >= 0 and not panelsorted: if meshes[i].faces: for j in range(len(meshes[i].faces)): if meshes[i].faces[j].kosher: mesh = MyMesh(meshes[i].name) mesh.faces.append(meshes[i].faces[j]) meshes[i].faces[j] = 0 # Remove original face meshes.append(mesh) panelsorted = 1 break i = i - 1 # 2nd to 2**n+1th pass: Output meshes for passno in range(npasses): strips = [] for i in range(len(meshes)): Window.DrawProgressBar( float(nobj) / objlen, "Exporting %s%% ..." % (nobj * 100 / objlen)) nobj = nobj + 1 if meshes[i]: self.makeStrips(strips, meshes[i], passno) for (strip, firstvertex, name) in strips: if len(strip) > 1 and len(strip[0].v) == 3: self.writeStrip(strip, firstvertex, name) for (strip, firstvertex, name) in strips: if len(strip) == 1 and len(strip[0].v) == 3: self.writeStrip(strip, firstvertex, name) for (strip, firstvertex, name) in strips: if len(strip) > 1 and len(strip[0].v) == 4: self.writeStrip(strip, firstvertex, name) for (strip, firstvertex, name) in strips: if len(strip) == 1 and len(strip[0].v) == 4: self.writeStrip(strip, firstvertex, name) # last pass: Output Lines and Lamps for o in range(len(theObjects) - 1, -1, -1): object = theObjects[o] if not object.Layer & layer: continue Window.DrawProgressBar( float(nobj) / objlen, "Exporting %s%% ..." % (nobj * 100 / objlen)) nobj = nobj + 1 objType = object.getType() if objType == "Mesh" and isLine(object, self.linewidth): self.writeLine(object) elif objType == "Lamp": self.writeLamp(object) self.file.write("end\t\t\t// eof\n\n") self.file.write( "// Built with Blender %4.2f. Exported with XPlane2Blender %s.\n" % (float(Blender.Get('version')) / 100, self.version))
def packIslands(islandList): if USER_FILL_HOLES: Window.DrawProgressBar(0.1, 'Merging Islands (Ctrl: skip merge)...') mergeUvIslands(islandList) # Modify in place # Now we have UV islands, we need to pack them. # Make a synchronised list with the islands # so we can box pak the islands. packBoxes = [] # Keep a list of X/Y offset so we can save time by writing the # uv's and packed data in one pass. islandOffsetList = [] islandIdx = 0 while islandIdx < len(islandList): minx, miny, maxx, maxy = boundsIsland(islandList[islandIdx]) w, h = maxx - minx, maxy - miny if USER_ISLAND_MARGIN: minx -= USER_ISLAND_MARGIN # *w miny -= USER_ISLAND_MARGIN # *h maxx += USER_ISLAND_MARGIN # *w maxy += USER_ISLAND_MARGIN # *h # recalc width and height w, h = maxx - minx, maxy - miny if w < 0.00001 or h < 0.00001: del islandList[islandIdx] islandIdx -= 1 continue '''Save the offset to be applied later, we could apply to the UVs now and allign them to the bottom left hand area of the UV coords like the box packer imagines they are but, its quicker just to remember their offset and apply the packing and offset in 1 pass ''' islandOffsetList.append((minx, miny)) # Add to boxList. use the island idx for the BOX id. packBoxes.append([0, 0, w, h]) islandIdx += 1 # Now we have a list of boxes to pack that syncs # with the islands. #print '\tPacking UV Islands...' Window.DrawProgressBar(0.7, 'Packing %i UV Islands...' % len(packBoxes)) time1 = sys.time() packWidth, packHeight = Geometry.BoxPack2D(packBoxes) # print 'Box Packing Time:', sys.time() - time1 #if len(pa ckedLs) != len(islandList): # raise "Error packed boxes differes from original length" #print '\tWriting Packed Data to faces' Window.DrawProgressBar(0.8, 'Writing Packed Data to faces') # Sort by ID, so there in sync again islandIdx = len(islandList) # Having these here avoids devide by 0 if islandIdx: if USER_STRETCH_ASPECT: # Maximize to uv area?? Will write a normalize function. xfactor = 1.0 / packWidth yfactor = 1.0 / packHeight else: # Keep proportions. xfactor = yfactor = 1.0 / max(packWidth, packHeight) while islandIdx: islandIdx -= 1 # Write the packed values to the UV's xoffset = packBoxes[islandIdx][0] - islandOffsetList[islandIdx][0] yoffset = packBoxes[islandIdx][1] - islandOffsetList[islandIdx][1] for f in islandList[ islandIdx]: # Offsetting the UV's so they fit in there packed box for uv in f.uv: uv.x = (uv.x + xoffset) * xfactor uv.y = (uv.y + yoffset) * yfactor
def mergeUvIslands(islandList): global USER_FILL_HOLES global USER_FILL_HOLES_QUALITY # Pack islands to bottom LHS # Sync with island #islandTotFaceArea = [] # A list of floats, each island area #islandArea = [] # a list of tuples ( area, w,h) decoratedIslandList = [] islandIdx = len(islandList) while islandIdx: islandIdx -= 1 minx, miny, maxx, maxy = boundsIsland(islandList[islandIdx]) w, h = maxx - minx, maxy - miny totFaceArea = 0 offset = Vector(minx, miny) for f in islandList[islandIdx]: for uv in f.uv: uv -= offset totFaceArea += f.area islandBoundsArea = w * h efficiency = abs(islandBoundsArea - totFaceArea) # UV Edge list used for intersections as well as unique points. edges, uniqueEdgePoints = island2Edge(islandList[islandIdx]) decoratedIslandList.append([ islandList[islandIdx], totFaceArea, efficiency, islandBoundsArea, w, h, edges, uniqueEdgePoints ]) # Sort by island bounding box area, smallest face area first. # no.. chance that to most simple edge loop first. decoratedIslandListAreaSort = decoratedIslandList[:] try: decoratedIslandListAreaSort.sort(key=lambda A: A[3]) except: decoratedIslandListAreaSort.sort(lambda A, B: cmp(A[3], B[3])) # sort by efficiency, Least Efficient first. decoratedIslandListEfficSort = decoratedIslandList[:] # decoratedIslandListEfficSort.sort(lambda A, B: cmp(B[2], A[2])) try: decoratedIslandListEfficSort.sort(key=lambda A: -A[2]) except: decoratedIslandListEfficSort.sort(lambda A, B: cmp(B[2], A[2])) # ================================================== THESE CAN BE TWEAKED. # This is a quality value for the number of tests. # from 1 to 4, generic quality value is from 1 to 100 USER_STEP_QUALITY = ((USER_FILL_HOLES_QUALITY - 1) / 25.0) + 1 # If 100 will test as long as there is enough free space. # this is rarely enough, and testing takes a while, so lower quality speeds this up. # 1 means they have the same quality USER_FREE_SPACE_TO_TEST_QUALITY = 1 + (( (100 - USER_FILL_HOLES_QUALITY) / 100.0) * 5) #print 'USER_STEP_QUALITY', USER_STEP_QUALITY #print 'USER_FREE_SPACE_TO_TEST_QUALITY', USER_FREE_SPACE_TO_TEST_QUALITY removedCount = 0 areaIslandIdx = 0 ctrl = Window.Qual.CTRL BREAK = False while areaIslandIdx < len(decoratedIslandListAreaSort) and not BREAK: sourceIsland = decoratedIslandListAreaSort[areaIslandIdx] # Alredy packed? if not sourceIsland[0]: areaIslandIdx += 1 else: efficIslandIdx = 0 while efficIslandIdx < len( decoratedIslandListEfficSort) and not BREAK: if Window.GetKeyQualifiers() & ctrl: BREAK = True break # Now we have 2 islands, is the efficience of the islands lowers theres an # increasing likely hood that we can fit merge into the bigger UV island. # this ensures a tight fit. # Just use figures we have about user/unused area to see if they might fit. targetIsland = decoratedIslandListEfficSort[efficIslandIdx] if sourceIsland[0] == targetIsland[0] or\ not targetIsland[0] or\ not sourceIsland[0]: pass else: # ([island, totFaceArea, efficiency, islandArea, w,h]) # Waisted space on target is greater then UV bounding island area. # if targetIsland[3] > (sourceIsland[2]) and\ # # print USER_FREE_SPACE_TO_TEST_QUALITY, 'ass' if targetIsland[2] > (sourceIsland[1] * USER_FREE_SPACE_TO_TEST_QUALITY) and\ targetIsland[4] > sourceIsland[4] and\ targetIsland[5] > sourceIsland[5]: # DEBUG # print '%.10f %.10f' % (targetIsland[3], sourceIsland[1]) # These enough spare space lets move the box until it fits # How many times does the source fit into the target x/y blockTestXUnit = targetIsland[4] / sourceIsland[4] blockTestYUnit = targetIsland[5] / sourceIsland[5] boxLeft = 0 # Distllllance we can move between whilst staying inside the targets bounds. testWidth = targetIsland[4] - sourceIsland[4] testHeight = targetIsland[5] - sourceIsland[5] # Increment we move each test. x/y xIncrement = (testWidth / (blockTestXUnit * ((USER_STEP_QUALITY / 50) + 0.1))) yIncrement = (testHeight / (blockTestYUnit * ((USER_STEP_QUALITY / 50) + 0.1))) # Make sure were not moving less then a 3rg of our width/height if xIncrement < sourceIsland[4] / 3: xIncrement = sourceIsland[4] if yIncrement < sourceIsland[5] / 3: yIncrement = sourceIsland[5] boxLeft = 0 # Start 1 back so we can jump into the loop. boxBottom = 0 #-yIncrement ##testcount= 0 while boxBottom <= testHeight: # Should we use this? - not needed for now. #if Window.GetKeyQualifiers() & ctrl: # BREAK= True # break ##testcount+=1 #print 'Testing intersect' Intersect = islandIntersectUvIsland( sourceIsland, targetIsland, Vector(boxLeft, boxBottom)) #print 'Done', Intersect if Intersect == 1: # Line intersect, dont bother with this any more pass if Intersect == 2: # Source inside target ''' We have an intersection, if we are inside the target then move us 1 whole width accross, Its possible this is a bad idea since 2 skinny Angular faces could join without 1 whole move, but its a lot more optimal to speed this up since we have alredy tested for it. It gives about 10% speedup with minimal errors. ''' #print 'ass' # Move the test allong its width + SMALL_NUM #boxLeft += sourceIsland[4] + SMALL_NUM boxLeft += sourceIsland[4] elif Intersect == 0: # No intersection?? Place it. # Progress removedCount += 1 Window.DrawProgressBar( 0.0, 'Merged: %i islands, Ctrl to finish early.' % removedCount) # Move faces into new island and offset targetIsland[0].extend(sourceIsland[0]) offset = Vector(boxLeft, boxBottom) for f in sourceIsland[0]: for uv in f.uv: uv += offset sourceIsland[0][:] = [] # Empty # Move edge loop into new and offset. # targetIsland[6].extend(sourceIsland[6]) #while sourceIsland[6]: targetIsland[6].extend( [ (\ (e[0]+offset, e[1]+offset, e[2])\ ) for e in sourceIsland[6] ] ) sourceIsland[6][:] = [] # Empty # Sort by edge length, reverse so biggest are first. try: targetIsland[6].sort(key=lambda A: A[2]) except: targetIsland[6].sort( lambda B, A: cmp(A[2], B[2])) targetIsland[7].extend(sourceIsland[7]) offset = Vector(boxLeft, boxBottom, 0) for p in sourceIsland[7]: p += offset sourceIsland[7][:] = [] # Decrement the efficiency targetIsland[1] += sourceIsland[ 1] # Increment totFaceArea targetIsland[2] -= sourceIsland[ 1] # Decrement efficiency # IF we ever used these again, should set to 0, eg sourceIsland[ 2] = 0 # No area if anyone wants to know break # INCREMENR NEXT LOCATION if boxLeft > testWidth: boxBottom += yIncrement boxLeft = 0.0 else: boxLeft += xIncrement ##print testcount efficIslandIdx += 1 areaIslandIdx += 1 # Remove empty islands i = len(islandList) while i: i -= 1 if not islandList[i]: del islandList[i] # Can increment islands removed here.
def main(arg): print('\nStarting morph copy...') # get selected meshes scn = Blender.Scene.GetCurrent() obs = [ob for ob in self.context.selected_objects if ob.type == 'MESH'] if not obs: Blender.Draw.PupMenu( 'Error%t|2 or more mesh objects need to be selected.|aborting.') return PREF_QUALITY = Blender.Draw.Create(0) PREF_NO_XCROSS = Blender.Draw.Create(0) PREF_SEL_ONLY = Blender.Draw.Create(0) pup_block = [ ('Quality:', PREF_QUALITY, 0, 4, 'Generate interpolated verts for a higher quality result.'), ('No X Crossing', PREF_NO_XCROSS, 'Do not snap across the zero X axis'), '', '"Update Selected" copies', 'active object weights to', 'selected verts on the other', 'selected mesh objects.', ('Update Selected', PREF_SEL_ONLY, 'Only copy new morphs to selected verts on the target mesh. ' '(use active object as source)'), ] if not Blender.Draw.PupBlock("Copy morphs for %i meshes" % len(obs), pup_block): return PREF_SEL_ONLY = PREF_SEL_ONLY.val PREF_NO_XCROSS = PREF_NO_XCROSS.val PREF_QUALITY = PREF_QUALITY.val # XXX quality ignored for now due to bug in subdivide if PREF_QUALITY: print('\tQuality not yet implemented due to subdivide bug in Blender.') print('\tFalling back on quality level 0.') PREF_QUALITY = 0 act_ob = scn.objects.active if PREF_SEL_ONLY and (act_ob is None): Blender.Draw.PupMenu( 'Error%t|When dealing with 2 or more meshes with morphs|There must be an active object|to be used as a source|aborting.' ) return # saves editmode state and exit editmode if it is enabled # (cannot make changes mesh data in editmode) is_editmode = Window.EditMode() Window.EditMode(0) Window.WaitCursor(1) t = sys.time() sel = [] from_data = None for ob in obs: me = ob.getData(mesh=1) morph_key = me.key # If this is the only mesh with a morph key OR if its one of # many, but its active. if (morph_key and ((ob is act_ob and PREF_SEL_ONLY) or (not PREF_SEL_ONLY))): if from_data: Blender.Draw.PupMenu( 'More then 1 mesh has morphs, only select 1 mesh with ' 'morphs. Aborting.') return else: # This uses worldspace_verts_idx which gets (idx,co) # pairs, then zsorts. if PREF_QUALITY: for _ob in obs: _ob.sel = 0 ob.sel = 1 Object.Duplicate(mesh=1) ob = scn.objects.active me = ob.getData(mesh=1) # morphs will be the same print(('\tGenerating higher %ix quality weights.' % PREF_QUALITY)) subdivMesh(me, PREF_QUALITY) scn.unlink(ob) from_data = (ob, me, worldspace_verts_zsort(me, ob), morph_key) else: data = (ob, me, worldspace_verts(me, ob), morph_key) sel.append(data) if not from_data: Blender.Draw.PupMenu('Error%t|No mesh with morphs found.') return if not sel: Blender.Draw.PupMenu( 'Error%t|Select 2 or more mesh objects, aborting.') # We can't unlink the mesh, but at least remove its data. if PREF_QUALITY: from_data[1].vertices = None return # Now do the copy. print(('\tCopying from "%s" to %i other mesh(es).' % (from_data[0].name, len(sel)))) for data in sel: copy_morphs(from_data, data, PREF_SEL_ONLY, PREF_NO_XCROSS) # We can't unlink the mesh, but at least remove its data. if PREF_QUALITY: from_data[1].vertices = None print(('Morph copy finished in %.2f seconds' % (sys.time() - t))) Window.DrawProgressBar(1.0, '') Window.WaitCursor(0) if is_editmode: Window.EditMode(1)
def readDSF(path): baddsf=(0, "Invalid DSF file", path) h=file(path, 'rb') h.seek(0, 2) hlen=h.tell() h.seek(0, 0) if h.read(8)!='XPLNEDSF' or unpack('<I',h.read(4))!=(1,) or h.read(4)!='DAEH': raise IOError, baddsf (l,)=unpack('<I', h.read(4)) headend=h.tell()+l-8 if h.read(4)!='PORP': raise IOError, baddsf (l,)=unpack('<I', h.read(4)) properties=[] c=h.read(l-9).split('\0') h.read(1) overlay=0 for i in range(0, len(c)-1, 2): if c[i]=='sim/overlay': overlay=int(c[i+1]) elif c[i]=='sim/south': lat=int(c[i+1]) elif c[i]=='sim/west': lon=int(c[i+1]) properties.append((c[i],c[i+1])) h.seek(headend) if overlay: # Overlay DSF - bail early h.close() raise IOError, (0, "This is an overlay DSF", path) # Definitions Atom if h.read(4)!='NFED': raise IOError, baddsf (l,)=unpack('<I', h.read(4)) defnend=h.tell()+l-8 terrain=objects=polygons=network=[] while h.tell()<defnend: c=h.read(4) (l,)=unpack('<I', h.read(4)) if l==8: pass # empty elif c=='TRET': terrain=h.read(l-9).replace('\\','/').replace(':','/').split('\0') h.read(1) elif c=='TJBO': objects=h.read(l-9).replace('\\','/').replace(':','/').split('\0') h.read(1) elif c=='YLOP': polygons=h.read(l-9).replace('\\','/').replace(':','/').split('\0') h.read(1) elif c=='WTEN': networks=h.read(l-9).replace('\\','/').replace(':','/').split('\0') h.read(1) else: h.seek(l-8, 1) # Geodata Atom if h.read(4)!='DOEG': raise IOError, baddsf (l,)=unpack('<I', h.read(4)) geodend=h.tell()+l-8 pool=[] scal=[] while h.tell()<geodend: c=h.read(4) (l,)=unpack('<I', h.read(4)) if c=='LOOP': thispool=[] (n,)=unpack('<I', h.read(4)) (p,)=unpack('<B', h.read(1)) for i in range(p): thisplane=[] (e,)=unpack('<B', h.read(1)) if e==0 or e==1: last=0 for j in range(n): (d,)=unpack('<H', h.read(2)) if e==1: d=(last+d)&65535 thisplane.append(d) last=d elif e==2 or e==3: last=0 while(len(thisplane))<n: (r,)=unpack('<B', h.read(1)) if (r&128): (d,)=unpack('<H', h.read(2)) for j in range(r&127): if e==3: thisplane.append((last+d)&65535) last=(last+d)&65535 else: thisplane.append(d) else: for j in range(r): (d,)=unpack('<H', h.read(2)) if e==3: d=(last+d)&65535 thisplane.append(d) last=d else: raise IOError, baddsf thispool.append(thisplane) pool.append(thispool) elif c=='LACS': thisscal=[] for i in range(0, l-8, 8): d=unpack('<2f', h.read(8)) thisscal.append(d) scal.append(thisscal) else: h.seek(l-8, 1) # Rescale pool and transform to one list per entry if len(scal)!=len(pool): raise(IOError) newpool=[] for i in range(len(pool)): curpool=pool[i] n=len(curpool[0]) newpool=[[] for j in range(n)] for plane in range(len(curpool)): (scale,offset)=scal[i][plane] scale=scale/65535 for j in range(n): newpool[j].append(curpool[plane][j]*scale+offset) pool[i]=newpool # Commands Atom if h.read(4)!='SDMC': raise IOError, baddsf (l,)=unpack('<I', h.read(4)) cmdsend=h.tell()+l-8 curpool=0 idx=0 near=0 far=-1 flags=0 # 0=physical, 1=overlay f=[[[],[]] for i in range(len(terrain))] v=[[[],[]] for i in range(len(terrain))] t=[[[],[]] for i in range(len(terrain))] pscale=99.0/(hlen-geodend) progress=0 while h.tell()<cmdsend: now=int((h.tell()-geodend)*pscale) if progress!=now: progress=now Window.DrawProgressBar(progress/100.0, "Importing %2d%%"%progress) (c,)=unpack('<B', h.read(1)) if c==1: # Coordinate Pool Select (curpool,)=unpack('<H', h.read(2)) elif c==2: # Junction Offset Select h.read(4) # not implemented elif c==3: # Set Definition (idx,)=unpack('<B', h.read(1)) elif c==4: # Set Definition (idx,)=unpack('<H', h.read(2)) elif c==5: # Set Definition (idx,)=unpack('<I', h.read(4)) elif c==6: # Set Road Subtype h.read(1) # not implemented elif c==7: # Object h.read(2) # not implemented elif c==8: # Object Range h.read(4) # not implemented elif c==9: # Network Chain (l,)=unpack('<B', h.read(1)) h.read(l*2) # not implemented elif c==10: # Network Chain Range h.read(4) # not implemented elif c==11: # Network Chain (l,)=unpack('<B', h.read(1)) h.read(l*4) # not implemented elif c==12: # Polygon (param,l)=unpack('<HB', h.read(3)) h.read(l*2) # not implemented elif c==13: # Polygon Range (DSF2Text uses this one) (param,first,last)=unpack('<HHH', h.read(6)) # not implemented elif c==14: # Nested Polygon (param,n)=unpack('<HB', h.read(3)) for i in range(n): (l,)=unpack('<B', h.read(1)) h.read(l*2) # not implemented elif c==15: # Nested Polygon Range (DSF2Text uses this one too) (param,n)=unpack('<HB', h.read(3)) h.read((n+1)*2) # not implemented elif c==16: # Terrain Patch pass elif c==17: # Terrain Patch w/ flags (flags,)=unpack('<B', h.read(1)) flags-=1 elif c==18: # Terrain Patch w/ flags & LOD (flags,near,far)=unpack('<Bff', h.read(9)) flags-=1 elif c==23: # Patch Triangle (l,)=unpack('<B', h.read(1)) n=len(v[idx][flags]) for i in range(n,n+l,3): f[idx][flags].append([i+2,i+1,i]) for i in range(l): (d,)=unpack('<H', h.read(2)) p=pool[curpool][d] v[idx][flags].append([(p[0]-lon)*hscale, (p[1]-lat)*hscale, p[2]*vscale]) if len(p)>=7: t[idx][flags].append([p[5],p[6]]) elif c==24: # Patch Triangle - cross-pool (l,)=unpack('<B', h.read(1)) n=len(v[idx][flags]) for i in range(n,n+l,3): f[idx][flags].append([i+2,i+1,i]) for i in range(l): (c,d)=unpack('<HH', h.read(4)) p=pool[c][d] v[idx][flags].append([(p[0]-lon)*hscale, (p[1]-lat)*hscale, p[2]*vscale]) if len(p)>=7: t[idx][flags].append([p[5],p[6]]) elif c==25: # Patch Triangle Range (first,last)=unpack('<HH', h.read(4)) n=len(v[idx][flags]) for i in range(n,n+last-first,3): f[idx][flags].append([i+2,i+1,i]) for d in range(first,last): p=pool[curpool][d] v[idx][flags].append([(p[0]-lon)*hscale, (p[1]-lat)*hscale, p[2]*vscale]) if len(p)>=7: t[idx][flags].append([p[5],p[6]]) #elif c==26: # Patch Triangle Strip (not used by DSF2Text) #elif c==27: #elif c==28: elif c==29: # Patch Triangle Fan (l,)=unpack('<B', h.read(1)) n=len(v[idx][flags]) for i in range(1,l-1): f[idx][flags].append([n+i+1,n+i,n]) for i in range(l): (d,)=unpack('<H', h.read(2)) p=pool[curpool][d] v[idx][flags].append([(p[0]-lon)*hscale, (p[1]-lat)*hscale, p[2]*vscale]) if len(p)>=7: t[idx][flags].append([p[5],p[6]]) elif c==30: # Patch Triangle Fan - cross-pool (l,)=unpack('<B', h.read(1)) n=len(v[idx][flags]) for i in range(1,l-1): f[idx][flags].append([n+i+1,n+i,n]) for i in range(l): (c,d)=unpack('<HH', h.read(4)) p=pool[c][d] v[idx][flags].append([(p[0]-lon)*hscale, (p[1]-lat)*hscale, p[2]*vscale]) if len(p)>=7: t[idx][flags].append([p[5],p[6]]) elif c==31: # Patch Triangle Fan Range (first,last)=unpack('<HH', h.read(4)) n=len(v[idx][flags]) for i in range(1,last-first-1): f[idx][flags].append([n+i+1,n+i,n]) for d in range(first, last): p=pool[curpool][d] v[idx][flags].append([(p[0]-lon)*hscale, (p[1]-lat)*hscale, p[2]*vscale]) if len(p)>=7: t[idx][flags].append([p[5],p[6]]) elif c==32: # Comment (l,)=unpack('<B', h.read(1)) h.read(l) elif c==33: # Comment (l,)=unpack('<H', h.read(2)) h.read(l) elif c==34: # Comment (l,)=unpack('<I', h.read(4)) h.read(l) else: raise IOError, (c, "Unrecognised command (%d)" % c, c) h.close() Window.DrawProgressBar(0.99, "Realising") scene=Scene.GetCurrent() scene.layers=[1,2] for flags in [0]:# was [1,0]: # overlay first so overlays for idx in range(len(terrain)): if not f[idx][flags]: continue if idx: name=basename(terrain[idx])[:-4] if flags: name=name+'.2' if terrain[idx] in libterrain: (texture, angle, xscale, zscale)=readTER(libterrain[terrain[idx]]) elif exists(join(dirname(path), pardir, pardir, terrain[idx])): (texture, angle, xscale, zscale)=readTER(abspath(join(dirname(path), pardir, pardir, terrain[idx]))) else: raise IOError(0, 'Terrain %s not found' % terrain[idx], terrain[idx]) try: mat=Material.Get(name) except: mat=Material.New(name) mat.rgbCol=[1.0, 1.0, 1.0] mat.spec=0 try: img=Image.Get(basename(texture)) except: img=Image.Load(texture) tex=Texture.New(name) tex.setType('Image') tex.image=img mat.setTexture(0, tex) if flags: mat.zOffset=1 mat.mode |= Material.Modes.ZTRANSP mtex=mat.getTextures()[0] mtex.size=(xscale*250, zscale*250, 0) mtex.zproj=Texture.Proj.NONE if t[idx][flags]: mtex.texco=Texture.TexCo.UV else: mtex.texco=Texture.TexCo.GLOB else: name=terrain[idx] mat=Material.New(terrain[idx]) mat.rgbCol=[0.1, 0.1, 0.2] mat.spec=0 mesh=Mesh.New(name) mesh.mode &= ~(Mesh.Modes.TWOSIDED|Mesh.Modes.AUTOSMOOTH) mesh.mode |= Mesh.Modes.NOVNORMALSFLIP mesh.materials += [mat] mesh.verts.extend(v[idx][flags]) mesh.faces.extend(f[idx][flags]) if t[idx][flags]: faceno=0 for face in mesh.faces: face.uv=[Vector(t[idx][flags][i][0], t[idx][flags][i][1]) for i in f[idx][flags][faceno]] face.image=img faceno+=1 mesh.update() ob = Object.New("Mesh", name) ob.link(mesh) scene.objects.link(ob) ob.Layer=flags+1 ob.addProperty('terrain', terrain[idx]) mesh.sel=True mesh.remDoubles(0.001) # must be after linked to object mesh.sel=False if 0: # Unreliable for face in mesh.faces: for v in face.verts: if v.co[2]!=0.0: break else: face.mat=1 # water lamp=Lamp.New("Lamp", "Sun") ob = Object.New("Lamp", "Sun") ob.link(lamp) scene.objects.link(ob) lamp.type=1 ob.Layer=3 ob.setLocation(500, 500, 1000)
def file_callback (filename): try: bgl=file(filename,'rb') except: Draw.PupMenu("ERROR%%t|Can't open %s" % filename) return bgldir=dirname(filename) guid=None friendly=None texnames=[] # list of texture file names matlist=[] # fs9 materials mats=[] # list of Blender Materials inde=[] vert=[] tran=[] amap=[] # fsx map scen=[] # (child, peer, matrix, parent) data={} # (material, vert, inde, scene) by LOD plat={} # (surface, vertices) by scene atta=[] # (name, scene) attobjs=[] atteffects=[] partcount=0 Window.WaitCursor(1) Window.DrawProgressBar(0, "Opening ...") try: (c,size,endmdl)=container(bgl,0) assert (c=='RIFF') assert (bgl.read(4) in ['MDL9','MDLX']) while bgl.tell()<endmdl: (c,size,end1)=container(bgl,1) if c=='MDLG': # FSX guid guid='{%x-%x-%x-%x%x-%x%x%x%x%x%x}' % unpack('<IHH8B',bgl.read(size)) if debug: print guid elif c=='MDLH': if size==36: # FS9 header (size,reserved,reserved,radius,reserved,reserved,reserved,reserved,reserved)=unpack('<9I', bgl.read(size)) if debug: print radius else: bgl.seek(size,1) elif c=='MDLN': # FSX friendly name friendly=bgl.read(size).strip('\0').strip() if debug: print friendly elif c=='MDLD': # FSX data while bgl.tell()<end1: Window.DrawProgressBar(float(bgl.tell())/(endmdl+endmdl), "Reading ...") (c,size,end2)=container(bgl,2) if c=='TEXT': texnames=[bgl.read(64).strip('\0').strip() for i in range(0,size,64)] elif c=='MATE': mats.extend([getmaterialx(bgl.read(120), bgldir, texnames) for i in range(0,size,120)]) elif c=='INDE': # reverse order of vertices in each triangle for i in range(size/6): t=list(unpack('<3H', bgl.read(6))) t.reverse() inde.extend(t) elif c=='VERB': while bgl.tell()<end2: (c,size,end3)=container(bgl,3) if c=='VERT': vert.append([tuple([round(i,VROUND) for i in unpack('<8f',bgl.read(32))]) for j in range(0,size,32)]) else: bgl.seek(size,1) elif c=='TRAN': for i in range(0,size,64): tran.append(Matrix(*[unpack('<4f',bgl.read(16)) for j in range(4)])) elif c=='AMAP': for i in range(0,size,8): (a,b)=unpack('<2I',bgl.read(8)) amap.append(b) elif c=='SCEN': # Assumed to be after TRAN and AMAP sections count=size/8 for i in range(count): (child,peer,offset,unk)=unpack('<4h',bgl.read(8)) thismatrix=tran[amap[offset/8]] scen.append((child,peer,thismatrix,-1)) elif c=='LODT': while bgl.tell()<end2: (c,size,end3)=container(bgl,3) if c=='LODE': (lod,)=unpack('<I', bgl.read(4)) while bgl.tell()<end3: (c,size,end4)=container(bgl,4) if c=='PART': (typ,sceneg,material,verb,voff,vcount,ioff,icount,mouserect)=unpack('<9I', bgl.read(36)) if debug: print lod, typ,sceneg,material,verb,voff,vcount,ioff,icount,mouserect assert (typ==1) # TRIANGLE_LIST if not lod in data: data[lod]=[] data[lod].append((mats[material], vert[verb][voff:voff+vcount], inde[ioff:ioff+icount], sceneg)) partcount+=1 else: bgl.seek(size,1) else: bgl.seek(size,1) elif c=='PLAL': while bgl.tell()<end2: (c,size,end3)=container(bgl,3) if c=='PLAT': (surface,sceneg,numvert,v0x,v0y,v0z,v1x,v1y,v1z,v2x,v2y,v2z)=unpack('<3I9f', bgl.read(48)) assert (numvert==3) #print (surface,scene,numvert,v0x,v0y,v0z,v1x,v1y,v1z,v2x,v2y,v2z) if not sceneg in plat: plat[sceneg]={} plat[sceneg][((round(v2x,VROUND),round(v2y,VROUND),round(v2z,VROUND)),(round(v1x,VROUND),round(v1y,VROUND),round(v1z,VROUND)),(round(v0x,VROUND),round(v0y,VROUND),round(v0z,VROUND)))]=surface else: bgl.seek(size,1) elif c=='REFL': while bgl.tell()<end2: (c,size,end3)=container(bgl,3) if c=='REFP': (sceneg,size)=unpack('<II', bgl.read(8)) atta.append((bgl.read(size).strip('\0').strip(),sceneg)) else: bgl.seek(size,1) elif c=='ATTO': while bgl.tell()<end2: (unk,flags,size)=unpack('<IHH', bgl.read(8)) d=bgl.read(size) if flags==2: # Attached object attobjs.append((d[40:-5], '{%x-%x-%x-%x%x-%x%x%x%x%x%x}' % unpack('<IHH8B', d[20:36]))) elif flags==4: # Attached effect p=d[100:-5].split('\0') # params, attachpt atteffects.append((p[1], d[20:100].strip(' \0'), p[0])) elif debug: print "Unknown attach %d:\n%s" % (flags, d) else: bgl.seek(size,1) elif c=='EXTE': # FS9 data while bgl.tell()<end1: Window.DrawProgressBar(float(bgl.tell())/(endmdl+endmdl), "Reading ...") (c,size,end2)=container(bgl,2) if c=='TEXT': (bglop,count,reserved)=unpack('<HHI', bgl.read(8)) assert(bglop==0xb7) texnames=[unpack('<4I', bgl.read(16)) + (bgl.read(64).strip('\0').strip(),) for i in range(count)] assert (bgl.read(2)=='\x22\0') # return elif c=='MATE': (bglop,count,reserved)=unpack('<HHI', bgl.read(8)) assert(bglop==0xb6) matlist.extend([unpack('<17f', bgl.read(17*4)) for i in range(count)]) assert (bgl.read(2)=='\x22\0') # return elif c=='VERT': (bglop,count,reserved)=unpack('<HHI', bgl.read(8)) assert(bglop==0xb5) vert.extend([tuple([round(i,VROUND) for i in unpack('<8f',bgl.read(32))]) for j in range(count)]) assert (bgl.read(2)=='\x22\0') # return elif c=='BGL ': code=bgl.read(size) lods=[0] lodno=0 while lodno<len(lods): ip=0 lod=lods[lodno] sceneg=0 curmat=None stack=[] if debug: print "LOD >", lod while True: (bglop,)=unpack('<H', code[ip:ip+2]) if debug: print "%s%04x: %02x" % (' '*len(stack), ip, bglop) ip+=2 # mostly just opcodes from BGLFP.doc if bglop==0x0d: # BGL_JUMP (offset,)=unpack('<h', code[ip:ip+2]) ip=ip-2+offset elif bglop==0x22: # BGLOP_RETURN ip=stack.pop() elif bglop==0x23: # BGLOP_CALL (offset,)=unpack('<h', code[ip:ip+2]) stack.append(ip+2) ip=ip-2+offset elif bglop==0x24: # BGLOP_IFIN1 ip+=8 # assume true elif bglop==0x39: # BGLOP_IFMASK ip+=6 # assume true elif bglop==0x5f: # BGLOP_IFSIZEV (offset,r,pixels)=unpack('<hHH', code[ip:ip+6]) newlod=int(0.5+radius*2475.0/r) if newlod not in lods: lods.append(newlod) if lod<newlod: ip=ip-2+offset else: ip+=6 elif bglop==0x88: # BGLOP_JUMP_32 (offset,)=unpack('<i', code[ip:ip+4]) ip=ip-2+offset elif bglop==0x89: # BGLOP_JUMP_32 (offset,)=unpack('<i', code[ip:ip+4]) assert (offset==-1) ip=ip+4 elif bglop==0x8a: # BGLOP_CALL_32 (offset,)=unpack('<i', code[ip:ip+4]) stack.append(ip+4) ip=ip-2+offset elif bglop==0xa7: # BGLOP_SPRITE_VICALL ip+=20 # ignore elif bglop==0xb3: # BGLOP_IFINF1 ip+=14 # assume true elif bglop==0xb8: # BGLOP_SET_MATERIAL (m,t)=unpack('<hh', code[ip:ip+4]) ip+=4 curmat=getmaterial9(bgldir, m, matlist, t, texnames) elif bglop==0xb9: # BGLOP_DRAW_TRILIST (voff,vcount,icount)=unpack('<3H', code[ip:ip+6]) ip+=6 inde=unpack('<%dH' % icount, code[ip:ip+2*icount]) ip+=2*icount if debug: print "DATA:", lod, sceneg, voff,vcount,icount if not lod in data: data[lod]=[] data[lod].append((curmat, vert[voff:voff+vcount], inde, sceneg)) partcount+=1 elif bglop==0xbd: # BGLOP_END break elif bglop==0xc4: # BGLOP_SET_MATRIX_INDIRECT (sceneg,)=unpack('<H', code[ip:ip+2]) ip+=2 else: assert 0 lodno+=1 # Shift LODs up lods.sort() lods.append(100) for i in range(len(lods)-1,0,-1): data[lods[i]]=data[lods[i-1]] data[lods[0]].pop() elif c=='TRAN': for i in range(0,size,64): tran.append(Matrix(*[unpack('<4f',bgl.read(16)) for j in range(4)])) if debug: print i/64 print tran[i/64] elif c=='ANIC': anicbase=bgl.tell() amap=bgl.read(size) elif c=='SCEN': # Assumed to be after TRAN and ANIC sections (count,)=unpack('<H', bgl.read(2)) scen=[None for i in range(count)] for i in range(count): (n,child,peer,size,offset)=unpack('<4hi', bgl.read(12)) offset=bgl.tell()-12+offset-anicbase if size==6: # Static (bglop,src,dst)=unpack('<3H', amap[offset:offset+6]) assert (bglop==0xc6) thismatrix=tran[src] else: # Animation (x,y,z,dst)=unpack('<3fh', amap[offset+size-14:offset+size]) thismatrix=TranslationMatrix(Vector(x,y,z,0)) scen[n]=(child,peer,thismatrix,-1) elif c=='PLAT': (count,)=unpack('<I', bgl.read(4)) s=[] for i in range(count): (sceneg,offset,numvert,surface)=unpack('<HhHH', bgl.read(8)) assert (numvert==3) # triangle s.append((sceneg,surface)) # Assumes in order so can ignore offset for i in range(count): (sceneg,surface)=s[i] (v0x,v0y,v0z,v1x,v1y,v1z,v2x,v2y,v2z)=unpack('9f', bgl.read(36)) if not sceneg in plat: plat[sceneg]={} plat[sceneg][((round(v0x,VROUND),round(v0y,VROUND),round(v0z,VROUND)),(round(v1x,VROUND),round(v1y,VROUND),round(v1z,VROUND)),(round(v2x,VROUND),round(v2y,VROUND),round(v2z,VROUND)))]=surface elif c=='ATTA': (count,)=unpack('<I', bgl.read(4)) s=[] for i in range(count): (sceneg,offset)=unpack('<Hh', bgl.read(4)) s.append((sceneg)) # Assumes in order so can ignore offset for i in range(count): name='' while True: c=bgl.read(1) if c=='\0': break name=name+c atta.append((name.strip(),s[i])) elif c=='ATTO': # same as FSX while bgl.tell()<end2: (unk,flags,size)=unpack('<IHH', bgl.read(8)) d=bgl.read(size) if flags==2: # Attached object attobjs.append((d[40:-5], '{%x-%x-%x-%x%x-%x%x%x%x%x%x}' % unpack('<IHH8B', d[20:36]))) elif flags==4: # Attached effect p=d[100:-5].split('\0') # params, attachpt atteffects.append((p[1], d[20:100].strip(' \0'), p[0])) elif debug: print "Unknown attach %d:\n%s" % (flags, d) else: bgl.seek(size,1) else: bgl.seek(size,1) bgl.close() # Invert Child/Peer pointers to get parents for i in range(len(scen)): (child, peer, thismatrix, parent)=scen[i] if child!=-1: # child's parent is me (xchild, xpeer, xmatrix, xparent)=scen[child] scen[child]=(xchild, xpeer, xmatrix, i) if peer!=-1: # peer's parent is my parent assert (peer>i) (xchild, xpeer, xmatrix, xparent)=scen[peer] scen[peer]=(xchild, xpeer, xmatrix, parent) if debug: print "TRAN Matrices", len(tran) for i in range(len(tran)): print i print tran[i] #print "Animation map", len(amap) #for i in range(len(amap)): # print i, '->', amap[i] print "Scene Graph", len(scen) for i in range(len(scen)): (child, peer, thismatrix, parent)=scen[i] print i, child, peer, parent print thismatrix scene=Scene.GetCurrent() lods=data.keys() lods.sort() lods.reverse() partno=0.0 for layer in range(len(lods)): for (material, vert, inde, sceneg) in data[lods[layer]]: Window.DrawProgressBar(0.5+partno/(partcount+partcount), "Adding ...") (child, peer, finalmatrix, parent)=scen[sceneg] #print lods[layer] #print sceneg, child, peer, parent while parent!=-1: (child, peer, thismatrix, parent)=scen[parent] finalmatrix=finalmatrix*thismatrix #print finalmatrix if not layer and sceneg in plat: adddata(scene, layer+1, material, vert, inde, finalmatrix, plat[sceneg]) else: adddata(scene, layer+1, material, vert, inde, finalmatrix) partno+=1 if debug: for (sceneg,verts) in plat.iteritems(): if verts: print "Unallocated platforms: sceneg=%d, %d:" % (sceneg, len(verts.keys())) for v in verts.keys(): for vt in v: print "%.4f %.4f %.4f" % vt print # Attach points attachpoints={} for (name, sceneg) in atta: (child, peer, finalmatrix, parent)=scen[sceneg] while parent!=-1: (child, peer, thismatrix, parent)=scen[parent] finalmatrix=finalmatrix*thismatrix attachpoints[name]=addattach(scene, name, finalmatrix) for (name, obj) in attobjs: attachpoints[name].addProperty('guid', obj) for (name, effect, params) in atteffects: attachpoints[name].addProperty('effectName', effect) if params: attachpoints[name].addProperty('effectParams', params) addprops(scene, lods) Window.DrawProgressBar(1, "Finished") Window.WaitCursor(0) except: bgl.close() Window.DrawProgressBar(1, "ERROR") Window.WaitCursor(0) Draw.PupMenu("ERROR%%t|Can't read %s - is this a FSX MDL format file?" % filename)
def copy_bone_influences(_from, _to, PREF_SEL_ONLY, PREF_NO_XCROSS): ob_from, me_from, world_verts_from, from_groups = _from ob_to, me_to, world_verts_to, dummy = _to del dummy def getSnapIdx(seek_vec, vecs): ''' Returns the closest vec to snap_points ''' # First seek the closest Z axis vert idx/v seek_vec_x, seek_vec_y, seek_vec_z = seek_vec from_vec_idx = 0 len_vecs = len(vecs) upidx = len_vecs - 1 loidx = 0 while from_vec_idx < len_vecs and vecs[from_vec_idx][1].z < seek_vec_z: from_vec_idx += 1 # Clamp if we overstepped. if from_vec_idx >= len_vecs: from_vec_idx -= 1 close_dist = (vecs[from_vec_idx][1] - seek_vec).length close_idx = vecs[from_vec_idx][0] upidx = from_vec_idx + 1 loidx = from_vec_idx - 1 # Set uselo/useup. This means we can keep seeking up/down. if upidx >= len_vecs: useup = False else: useup = True if loidx < 0: uselo = False else: uselo = True # Seek up/down to find the closest v to seek vec. while uselo or useup: if useup: if upidx >= len_vecs: useup = False else: i, v = vecs[upidx] if (not PREF_NO_XCROSS) or ( (v.x >= -SMALL_NUM and seek_vec_x >= -SMALL_NUM) or (v.x <= SMALL_NUM and seek_vec_x <= SMALL_NUM)): # enfoce xcrossing if v.z - seek_vec_z > close_dist: # the verticle distance is greater then the best distance sofar. we can stop looking up. useup = False elif abs(seek_vec_y - v.y) < close_dist and abs( seek_vec_x - v.x) < close_dist: # This is in the limit measure it. l = (seek_vec - v).length if l < close_dist: close_dist = l close_idx = i upidx += 1 if uselo: if loidx == 0: uselo = False else: i, v = vecs[loidx] if (not PREF_NO_XCROSS) or ( (v.x >= -SMALL_NUM and seek_vec_x >= -SMALL_NUM) or (v.x <= SMALL_NUM and seek_vec_x <= SMALL_NUM)): # enfoce xcrossing if seek_vec_z - v.z > close_dist: # the verticle distance is greater then the best distance sofar. we can stop looking up. uselo = False elif abs(seek_vec_y - v.y) < close_dist and abs( seek_vec_x - v.x) < close_dist: # This is in the limit measure it. l = (seek_vec - v).length if l < close_dist: close_dist = l close_idx = i loidx -= 1 return close_idx to_groups = me_to.getVertGroupNames( ) # if not PREF_SEL_ONLY will always be [] from_groups = me_from.getVertGroupNames() if PREF_SEL_ONLY: # remove selected verts from all groups. vsel = [v.index for v in me_to.verts if v.sel] for group in to_groups: me_to.removeVertsFromGroup(group, vsel) else: # Add all groups. for group in from_groups: me_to.addVertGroup(group) add_ = Mesh.AssignModes.ADD for i, co in enumerate(world_verts_to): if (not PREF_SEL_ONLY) or (PREF_SEL_ONLY and me_to.verts[i].sel): Window.DrawProgressBar( 0.99 * (i / float(len(world_verts_to))), 'Copy "%s" -> "%s" ' % (ob_from.name, ob_to.name)) from_idx = getSnapIdx(co, world_verts_from) from_infs = me_from.getVertexInfluences(from_idx) for group, weight in from_infs: # Add where needed. if PREF_SEL_ONLY and group not in to_groups: me_to.addVertGroup(group) to_groups.append(group) me_to.assignVertsToGroup(group, [i], weight, add_) me_to.update()
def main(): print '\nStarting BoneWeight Copy...' scn = Blender.Scene.GetCurrent() contextSel = Object.GetSelected() if not contextSel: Blender.Draw.PupMenu( 'Error%t|2 or more mesh objects need to be selected.|aborting.') return PREF_QUALITY = Blender.Draw.Create(0) PREF_NO_XCROSS = Blender.Draw.Create(0) PREF_SEL_ONLY = Blender.Draw.Create(0) pup_block = [\ ('Quality:', PREF_QUALITY, 0, 4, 'Generate interpolated verts for a higher quality result.'),\ ('No X Crossing', PREF_NO_XCROSS, 'Do not snap across the zero X axis'),\ '',\ '"Update Selected" copies',\ 'active object weights to',\ 'selected verts on the other',\ 'selected mesh objects.',\ ('Update Selected', PREF_SEL_ONLY, 'Only copy new weights to selected verts on the target mesh. (use active object as source)'),\ ] if not Blender.Draw.PupBlock("Copy Weights for %i Meshs" % len(contextSel), pup_block): return PREF_SEL_ONLY = PREF_SEL_ONLY.val PREF_NO_XCROSS = PREF_NO_XCROSS.val quality = PREF_QUALITY.val act_ob = scn.objects.active if PREF_SEL_ONLY and act_ob == None: Blender.Draw.PupMenu( 'Error%t|When dealing with 2 or more meshes with vgroups|There must be an active object|to be used as a source|aborting.' ) return sel = [] from_data = None for ob in contextSel: if ob.type == 'Mesh': me = ob.getData(mesh=1) groups = me.getVertGroupNames() # If this is the only mesh with a group OR if its one of many, but its active. if groups and ((ob == act_ob and PREF_SEL_ONLY) or (not PREF_SEL_ONLY)): if from_data: Blender.Draw.PupMenu( 'More then 1 mesh has vertex weights, only select 1 mesh with weights. aborting.' ) return else: # This uses worldspace_verts_idx which gets (idx,co) pairs, then zsorts. if quality: for _ob in contextSel: _ob.sel = 0 ob.sel = 1 Object.Duplicate(mesh=1) ob = scn.objects.active me = ob.getData(mesh=1) # groups will be the same print '\tGenerating higher %ix quality weights.' % quality subdivMesh(me, quality) scn.unlink(ob) from_data = (ob, me, worldspace_verts_idx(me, ob), groups) else: data = (ob, me, worldspace_verts(me, ob), groups) sel.append(data) if not from_data: Blender.Draw.PupMenu('Error%t|No mesh with vertex groups found.') return if not sel: Blender.Draw.PupMenu( 'Error%t|Select 2 or more mesh objects, aborting.') if quality: from_data[1].verts = None return t = Blender.sys.time() Window.WaitCursor(1) # Now do the copy. print '\tCopying from "%s" to %i other mesh(es).' % (from_data[0].name, len(sel)) for data in sel: copy_bone_influences(from_data, data, PREF_SEL_ONLY, PREF_NO_XCROSS) # We cant unlink the mesh, but at least remove its data. if quality: from_data[1].verts = None print 'Copy Complete in %.6f sec' % (Blender.sys.time() - t) Window.DrawProgressBar(1.0, '') Window.WaitCursor(0)
def getUvIslands(faceGroups, me): # Get seams so we dont cross over seams edge_seams = {} # shoudl be a set SEAM = Mesh.EdgeFlags.SEAM for ed in me.edges: if ed.flag & SEAM: edge_seams[ed.key] = None # dummy var- use sets! # Done finding seams islandList = [] Window.DrawProgressBar( 0.0, 'Splitting %d projection groups into UV islands:' % len(faceGroups)) #print '\tSplitting %d projection groups into UV islands:' % len(faceGroups), # Find grouped faces faceGroupIdx = len(faceGroups) while faceGroupIdx: faceGroupIdx -= 1 faces = faceGroups[faceGroupIdx] if not faces: continue # Build edge dict edge_users = {} for i, f in enumerate(faces): for ed_key in f.edge_keys: if edge_seams.has_key(ed_key): # DELIMIT SEAMS! ;) edge_users[ed_key] = [] # so as not to raise an error else: try: edge_users[ed_key].append(i) except: edge_users[ed_key] = [i] # Modes # 0 - face not yet touched. # 1 - added to island list, and need to search # 2 - touched and searched - dont touch again. face_modes = [0] * len(faces) # initialize zero - untested. face_modes[0] = 1 # start the search with face 1 newIsland = [] newIsland.append(faces[0]) ok = True while ok: ok = True while ok: ok = False for i in xrange(len(faces)): if face_modes[i] == 1: # search for ed_key in faces[i].edge_keys: for ii in edge_users[ed_key]: if i != ii and face_modes[ii] == 0: face_modes[ii] = ok = 1 # mark as searched newIsland.append(faces[ii]) # mark as searched, dont look again. face_modes[i] = 2 islandList.append(newIsland) ok = False for i in xrange(len(faces)): if face_modes[i] == 0: newIsland = [] newIsland.append(faces[i]) face_modes[i] = ok = 1 break # if not ok will stop looping Window.DrawProgressBar( 0.1, 'Optimizing Rotation for %i UV Islands' % len(islandList)) for island in islandList: optiRotateUvIsland(island) return islandList
def main(): scn = Scene.GetCurrent() act_ob = scn.getActiveObject() if act_ob.getType() != 'Mesh': act_ob = None sel = [ ob for ob in Object.GetSelected() if ob.getType() == 'Mesh' if ob != act_ob ] if not sel and not act_ob: Draw.PupMenu('Error, select a mesh as your active object') return # Defaults PREF_EDITMESH_ONLY = Draw.Create(1) PREF_MIRROR_LOCATION = Draw.Create(1) PREF_XMID_SNAP = Draw.Create(1) PREF_MAX_DIST = Draw.Create(0.02) PREF_XZERO_THRESH = Draw.Create(0.002) #PREF_MODE= Draw.Create(0) # THIS IS TOOO CONFUSING, HAVE 2 BUTTONS AND MAKE THE MODE FROM THEM. PREF_MODE_L2R = Draw.Create(1) PREF_MODE_R2L = Draw.Create(0) PREF_SEL_ONLY = Draw.Create(1) PREF_EDGE_USERS = Draw.Create(0) # Weights PREF_MIRROR_WEIGHTS = Draw.Create(0) PREF_FLIP_NAMES = Draw.Create(1) PREF_CREATE_FLIP_NAMES = Draw.Create(1) pup_block = [\ ('EditMesh Only', PREF_EDITMESH_ONLY, 'If disabled, will mirror all selected meshes.'),\ 'Left (-), Right (+)',\ ('Left > Right', PREF_MODE_L2R, 'Copy from the Left to Right of the mesh. Enable Both for a mid loc/weight.'),\ ('Right > Left', PREF_MODE_R2L, 'Copy from the Right to Left of the mesh. Enable Both for a mid loc/weight.'),\ '',\ ('MaxDist:', PREF_MAX_DIST, 0.0, 1.0, 'Generate interpolated verts so closer vert weights can be copied.'),\ ('XZero limit:', PREF_XZERO_THRESH, 0.0, 1.0, 'Mirror verts above this distance from the middle, else lock to X/zero.'),\ ('Sel Verts Only', PREF_SEL_ONLY, 'Only mirror selected verts. Else try and mirror all'),\ ('Edge Users', PREF_EDGE_USERS, 'Only match up verts that have the same number of edge users.'),\ 'Location Prefs',\ ('Mirror Location', PREF_MIRROR_LOCATION, 'Mirror vertex locations.'),\ ('XMidSnap Verts', PREF_XMID_SNAP, 'Snap middle verts to X Zero (uses XZero limit)'),\ 'Weight Prefs',\ ('Mirror Weights', PREF_MIRROR_WEIGHTS, 'Mirror vertex locations.'),\ ('Flip Groups', PREF_FLIP_NAMES, 'Mirror flip names.'),\ ('New Flip Groups', PREF_CREATE_FLIP_NAMES, 'Make new groups for flipped names.'),\ ] if not Draw.PupBlock("X Mirror mesh tool", pup_block): return # WORK OUT THE MODE 0 # PREF_MODE, 0:middle, 1: Left. 2:Right. PREF_MODE_R2L = PREF_MODE_R2L.val PREF_MODE_L2R = PREF_MODE_L2R.val if PREF_MODE_R2L and PREF_MODE_L2R: PREF_MODE = 0 # Middle elif not PREF_MODE_R2L and PREF_MODE_L2R: PREF_MODE = 1 # Left to Right elif PREF_MODE_R2L and not PREF_MODE_L2R: PREF_MODE = 2 # Right to Left else: # Neither Selected. Do middle anyway PREF_MODE = 0 PREF_EDITMESH_ONLY = PREF_EDITMESH_ONLY.val PREF_MIRROR_LOCATION = PREF_MIRROR_LOCATION.val PREF_XMID_SNAP = PREF_XMID_SNAP.val PREF_MAX_DIST = PREF_MAX_DIST.val PREF_XZERO_THRESH = PREF_XZERO_THRESH.val PREF_SEL_ONLY = PREF_SEL_ONLY.val PREF_EDGE_USERS = PREF_EDGE_USERS.val # weights PREF_MIRROR_WEIGHTS = PREF_MIRROR_WEIGHTS.val PREF_FLIP_NAMES = PREF_FLIP_NAMES.val PREF_CREATE_FLIP_NAMES = PREF_CREATE_FLIP_NAMES.val t = sys.time() is_editmode = Window.EditMode() # Exit Editmode. if is_editmode: Window.EditMode(0) Mesh.Mode(Mesh.SelectModes['VERTEX']) Window.WaitCursor(1) if act_ob: mesh_mirror(act_ob.getData(mesh=1), PREF_MIRROR_LOCATION, PREF_XMID_SNAP, PREF_MAX_DIST, PREF_XZERO_THRESH, PREF_MODE, PREF_SEL_ONLY, PREF_EDGE_USERS, PREF_MIRROR_WEIGHTS, PREF_FLIP_NAMES, PREF_CREATE_FLIP_NAMES) if (not PREF_EDITMESH_ONLY) and sel: for ob in sel: mesh_mirror(ob.getData(mesh=1), PREF_MIRROR_LOCATION, PREF_XMID_SNAP, PREF_MAX_DIST, PREF_XZERO_THRESH, PREF_MODE, PREF_SEL_ONLY, PREF_EDGE_USERS, PREF_MIRROR_WEIGHTS, PREF_FLIP_NAMES, PREF_CREATE_FLIP_NAMES) if is_editmode: Window.EditMode(1) Window.WaitCursor(0) Window.DrawProgressBar(1.0, '') Window.RedrawAll() print 'Mirror done in %.6f sec.' % (sys.time() - t)
def main(): global USER_FILL_HOLES global USER_FILL_HOLES_QUALITY global USER_STRETCH_ASPECT global USER_ISLAND_MARGIN objects = bpy.data.scenes.active.objects # we can will tag them later. obList = [ob for ob in objects.context if ob.type == 'Mesh'] # Face select object may not be selected. ob = objects.active if ob and ob.sel == 0 and ob.type == 'Mesh': # Add to the list obList = [ob] del objects if not obList: Draw.PupMenu('error, no selected mesh objects') return # Create the variables. USER_PROJECTION_LIMIT = Draw.Create(66) USER_ONLY_SELECTED_FACES = Draw.Create(1) USER_SHARE_SPACE = Draw.Create(1) # Only for hole filling. USER_STRETCH_ASPECT = Draw.Create(1) # Only for hole filling. USER_ISLAND_MARGIN = Draw.Create(0.0) # Only for hole filling. USER_FILL_HOLES = Draw.Create(0) USER_FILL_HOLES_QUALITY = Draw.Create(50) # Only for hole filling. USER_VIEW_INIT = Draw.Create(0) # Only for hole filling. USER_AREA_WEIGHT = Draw.Create(1) # Only for hole filling. pup_block = [\ 'Projection',\ ('Angle Limit:', USER_PROJECTION_LIMIT, 1, 89, 'lower for more projection groups, higher for less distortion.'),\ ('Selected Faces Only', USER_ONLY_SELECTED_FACES, 'Use only selected faces from all selected meshes.'),\ ('Init from view', USER_VIEW_INIT, 'The first projection will be from the view vector.'),\ ('Area Weight', USER_AREA_WEIGHT, 'Weight projections vector by face area.'),\ '',\ '',\ '',\ 'UV Layout',\ ('Share Tex Space', USER_SHARE_SPACE, 'Objects Share texture space, map all objects into 1 uvmap.'),\ ('Stretch to bounds', USER_STRETCH_ASPECT, 'Stretch the final output to texture bounds.'),\ ('Island Margin:', USER_ISLAND_MARGIN, 0.0, 0.5, 'Margin to reduce bleed from adjacent islands.'),\ 'Fill in empty areas',\ ('Fill Holes', USER_FILL_HOLES, 'Fill in empty areas reduced texture waistage (slow).'),\ ('Fill Quality:', USER_FILL_HOLES_QUALITY, 1, 100, 'Depends on fill holes, how tightly to fill UV holes, (higher is slower)'),\ ] # Reuse variable if len(obList) == 1: ob = "Unwrap %i Selected Mesh" else: ob = "Unwrap %i Selected Meshes" # HACK, loop until mouse is lifted. ''' while Window.GetMouseButtons() != 0: sys.sleep(10) ''' if not Draw.PupBlock(ob % len(obList), pup_block): return del ob # Convert from being button types USER_PROJECTION_LIMIT = USER_PROJECTION_LIMIT.val USER_ONLY_SELECTED_FACES = USER_ONLY_SELECTED_FACES.val USER_SHARE_SPACE = USER_SHARE_SPACE.val USER_STRETCH_ASPECT = USER_STRETCH_ASPECT.val USER_ISLAND_MARGIN = USER_ISLAND_MARGIN.val USER_FILL_HOLES = USER_FILL_HOLES.val USER_FILL_HOLES_QUALITY = USER_FILL_HOLES_QUALITY.val USER_VIEW_INIT = USER_VIEW_INIT.val USER_AREA_WEIGHT = USER_AREA_WEIGHT.val USER_PROJECTION_LIMIT_CONVERTED = cos(USER_PROJECTION_LIMIT * DEG_TO_RAD) USER_PROJECTION_LIMIT_HALF_CONVERTED = cos( (USER_PROJECTION_LIMIT / 2) * DEG_TO_RAD) # Toggle Edit mode is_editmode = Window.EditMode() if is_editmode: Window.EditMode(0) # Assume face select mode! an annoying hack to toggle face select mode because Mesh dosent like faceSelectMode. if USER_SHARE_SPACE: # Sort by data name so we get consistant results try: obList.sort(key=lambda ob: ob.getData(name_only=1)) except: obList.sort(lambda ob1, ob2: cmp(ob1.getData(name_only=1), ob2.getData(name_only=1))) collected_islandList = [] Window.WaitCursor(1) time1 = sys.time() # Tag as False se we dont operate on teh same mesh twice. bpy.data.meshes.tag = False for ob in obList: me = ob.getData(mesh=1) if me.tag or me.lib: continue # Tag as used me.tag = True if not me.faceUV: # Mesh has no UV Coords, dont bother. me.faceUV = True if USER_ONLY_SELECTED_FACES: meshFaces = [thickface(f) for f in me.faces if f.sel] else: meshFaces = map(thickface, me.faces) if not meshFaces: continue Window.DrawProgressBar( 0.1, 'SmartProj UV Unwrapper, mapping "%s", %i faces.' % (me.name, len(meshFaces))) # ======= # Generate a projection list from face normals, this is ment to be smart :) # make a list of face props that are in sync with meshFaces # Make a Face List that is sorted by area. # meshFaces = [] # meshFaces.sort( lambda a, b: cmp(b.area , a.area) ) # Biggest first. try: meshFaces.sort(key=lambda a: -a.area) except: meshFaces.sort(lambda a, b: cmp(b.area, a.area)) # remove all zero area faces while meshFaces and meshFaces[-1].area <= SMALL_NUM: # Set their UV's to 0,0 for uv in meshFaces[-1].uv: uv.zero() meshFaces.pop() # Smallest first is slightly more efficient, but if the user cancels early then its better we work on the larger data. # Generate Projection Vecs # 0d is 1.0 # 180 IS -0.59846 # Initialize projectVecs if USER_VIEW_INIT: # Generate Projection projectVecs = [ Vector(Window.GetViewVector()) * ob.matrixWorld.copy().invert().rotationPart() ] # We add to this allong the way else: projectVecs = [] newProjectVec = meshFaces[0].no newProjectMeshFaces = [] # Popping stuffs it up. # Predent that the most unique angke is ages away to start the loop off mostUniqueAngle = -1.0 # This is popped tempMeshFaces = meshFaces[:] # This while only gathers projection vecs, faces are assigned later on. while 1: # If theres none there then start with the largest face # add all the faces that are close. for fIdx in xrange(len(tempMeshFaces) - 1, -1, -1): # Use half the angle limit so we dont overweight faces towards this # normal and hog all the faces. if newProjectVec.dot(tempMeshFaces[fIdx].no ) > USER_PROJECTION_LIMIT_HALF_CONVERTED: newProjectMeshFaces.append(tempMeshFaces.pop(fIdx)) # Add the average of all these faces normals as a projectionVec averageVec = Vector(0, 0, 0) if USER_AREA_WEIGHT: for fprop in newProjectMeshFaces: averageVec += (fprop.no * fprop.area) else: for fprop in newProjectMeshFaces: averageVec += fprop.no if averageVec.x != 0 or averageVec.y != 0 or averageVec.z != 0: # Avoid NAN projectVecs.append(averageVec.normalize()) # Get the next vec! # Pick the face thats most different to all existing angles :) mostUniqueAngle = 1.0 # 1.0 is 0d. no difference. mostUniqueIndex = 0 # dummy for fIdx in xrange(len(tempMeshFaces) - 1, -1, -1): angleDifference = -1.0 # 180d difference. # Get the closest vec angle we are to. for p in projectVecs: temp_angle_diff = p.dot(tempMeshFaces[fIdx].no) if angleDifference < temp_angle_diff: angleDifference = temp_angle_diff if angleDifference < mostUniqueAngle: # We have a new most different angle mostUniqueIndex = fIdx mostUniqueAngle = angleDifference if mostUniqueAngle < USER_PROJECTION_LIMIT_CONVERTED: #print 'adding', mostUniqueAngle, USER_PROJECTION_LIMIT, len(newProjectMeshFaces) # Now weight the vector to all its faces, will give a more direct projection # if the face its self was not representive of the normal from surrounding faces. newProjectVec = tempMeshFaces[mostUniqueIndex].no newProjectMeshFaces = [tempMeshFaces.pop(mostUniqueIndex)] else: if len(projectVecs) >= 1: # Must have at least 2 projections break # If there are only zero area faces then its possible # there are no projectionVecs if not len(projectVecs): Draw.PupMenu( 'error, no projection vecs where generated, 0 area faces can cause this.' ) return faceProjectionGroupList = [[] for i in xrange(len(projectVecs))] # MAP and Arrange # We know there are 3 or 4 faces here for fIdx in xrange(len(meshFaces) - 1, -1, -1): fvec = meshFaces[fIdx].no i = len(projectVecs) # Initialize first bestAng = fvec.dot(projectVecs[0]) bestAngIdx = 0 # Cycle through the remaining, first alredy done while i - 1: i -= 1 newAng = fvec.dot(projectVecs[i]) if newAng > bestAng: # Reverse logic for dotvecs bestAng = newAng bestAngIdx = i # Store the area for later use. faceProjectionGroupList[bestAngIdx].append(meshFaces[fIdx]) # Cull faceProjectionGroupList, # Now faceProjectionGroupList is full of faces that face match the project Vecs list for i in xrange(len(projectVecs)): # Account for projectVecs having no faces. if not faceProjectionGroupList[i]: continue # Make a projection matrix from a unit length vector. MatProj = VectoMat(projectVecs[i]) # Get the faces UV's from the projected vertex. for f in faceProjectionGroupList[i]: f_uv = f.uv for j, v in enumerate(f.v): f_uv[j][:] = (MatProj * v.co)[:2] if USER_SHARE_SPACE: # Should we collect and pack later? islandList = getUvIslands(faceProjectionGroupList, me) collected_islandList.extend(islandList) else: # Should we pack the islands for this 1 object? islandList = getUvIslands(faceProjectionGroupList, me) packIslands(islandList) # update the mesh here if we need to. # We want to pack all in 1 go, so pack now if USER_SHARE_SPACE: Window.DrawProgressBar(0.9, "Box Packing for all objects...") packIslands(collected_islandList) print "Smart Projection time: %.2f" % (sys.time() - time1) # Window.DrawProgressBar(0.9, "Smart Projections done, time: %.2f sec." % (sys.time() - time1)) if is_editmode: Window.EditMode(1) Window.DrawProgressBar(1.0, "") Window.WaitCursor(0) Window.RedrawAll()
def mesh_mirror(me, PREF_MIRROR_LOCATION, PREF_XMID_SNAP, PREF_MAX_DIST, PREF_XZERO_THRESH, PREF_MODE, PREF_SEL_ONLY, PREF_EDGE_USERS, PREF_MIRROR_WEIGHTS, PREF_FLIP_NAMES, PREF_CREATE_FLIP_NAMES): ''' PREF_MIRROR_LOCATION, Will we mirror locations? PREF_XMID_SNAP, Should we snap verts to X-0? PREF_MAX_DIST, Maximum distance to test snapping verts. PREF_XZERO_THRESH, How close verts must be to the middle before they are considered X-Zero verts. PREF_MODE, 0:middle, 1: Left. 2:Right. PREF_SEL_ONLY, only snap the selection PREF_EDGE_USERS, match only verts with the same number of edge users. PREF_MIRROR_LOCATION, ''' # Operate on all verts if not PREF_SEL_ONLY: for v in me.verts: v.sel = 1 if PREF_EDGE_USERS: edge_users = [0] * len(me.verts) for ed in me.edges: edge_users[ed.v1.index] += 1 edge_users[ed.v2.index] += 1 if PREF_XMID_SNAP: # Do we snap locations at all? for v in me.verts: if v.sel: if abs(v.co.x) <= PREF_XZERO_THRESH: v.co.x = 0 v.sel = 0 # alredy de-selected verts neg_vts = [v for v in me.verts if v.sel and v.co.x < 0] pos_vts = [v for v in me.verts if v.sel and v.co.x > 0] else: # Use a small margin verts must be outside before we mirror them. neg_vts = [v for v in me.verts if v.sel if v.co.x < -PREF_XZERO_THRESH] pos_vts = [v for v in me.verts if v.sel if v.co.x > PREF_XZERO_THRESH] #*Mirror Location*********************************************************# if PREF_MIRROR_LOCATION: mirror_pairs = [] # allign the negative with the positive. flipvec = Mathutils.Vector() len_neg_vts = float(len(neg_vts)) for i1, nv in enumerate(neg_vts): if nv.sel: # we may alredy be mirrored, if so well be deselected nv_co = nv.co for i2, pv in enumerate(pos_vts): if pv.sel: # Enforce edge users. if not PREF_EDGE_USERS or edge_users[i1] == edge_users[ i2]: flipvec[:] = pv.co flipvec.x = -flipvec.x l = (nv_co - flipvec).length if l == 0.0: # Both are alredy mirrored so we dont need to think about them. # De-Select so we dont use again/ pv.sel = nv.sel = 0 # Record a match. elif l <= PREF_MAX_DIST: # We can adjust the length by the normal, now we know the length is under the limit. # DISABLED, WASNT VERY USEFULL ''' if PREF_NOR_WEIGHT>0: # Get the normal and flipm reuse flipvec flipvec[:]= pv.no flipvec.x= -flipvec.x try: ang= Mathutils.AngleBetweenVecs(nv.no, flipvec)/180.0 except: # on rare occasions angle between vecs will fail.- zero length vec. ang= 0 l=l*(1+(ang*PREF_NOR_WEIGHT)) ''' # Record the pairs for sorting to see who will get joined mirror_pairs.append((l, nv, pv)) # Update every 20 loops if i1 % 10 == 0: Window.DrawProgressBar( 0.8 * (i1 / len_neg_vts), 'Mirror verts %i of %i' % (i1, len_neg_vts)) Window.DrawProgressBar(0.9, 'Mirror verts: Updating locations') # Now we have a list of the pairs we might use, lets find the best and do them first. # de-selecting as we go. so we can makke sure not to mess it up. try: mirror_pairs.sort(key=lambda a: a[0]) except: mirror_pairs.sort(lambda a, b: cmp(a[0], b[0])) for dist, v1, v2 in mirror_pairs: # dist, neg, pos if v1.sel and v2.sel: if PREF_MODE == 0: # Middle flipvec[:] = v2.co # positive flipvec.x = -flipvec.x # negatve v2.co = v1.co = (flipvec + v1.co) * 0.5 # midway v2.co.x = -v2.co.x elif PREF_MODE == 2: # Left v2.co = v1.co v2.co.x = -v2.co.x elif PREF_MODE == 1: # Right v1.co = v2.co v1.co.x = -v1.co.x v1.sel = v2.sel = 0 #*Mirror Weights**********************************************************# if PREF_MIRROR_WEIGHTS: groupNames, vWeightDict = BPyMesh.meshWeight2Dict(me) mirror_pairs_l2r = [] # Stor a list of matches for these verts. mirror_pairs_r2l = [] # Stor a list of matches for these verts. # allign the negative with the positive. flipvec = Mathutils.Vector() len_neg_vts = float(len(neg_vts)) # Here we make a tuple to look through, if were middle well need to look through both. if PREF_MODE == 0: # Middle find_set = ((neg_vts, pos_vts, mirror_pairs_l2r), (pos_vts, neg_vts, mirror_pairs_r2l)) elif PREF_MODE == 1: # Left find_set = ((neg_vts, pos_vts, mirror_pairs_l2r), ) elif PREF_MODE == 2: # Right find_set = ((pos_vts, neg_vts, mirror_pairs_r2l), ) # Do a locational lookup again :/ - This isnt that good form but if we havnt mirrored weights well need to do it anyway. # The Difference with this is that we dont need to have 1:1 match for each vert- just get each vert to find another mirrored vert # and use its weight. # Use "find_set" so we can do a flipped search L>R and R>L without duplicate code. for vtls_A, vtls_B, pair_ls in find_set: for i1, vA in enumerate(vtls_A): best_len = 1 << 30 # BIGNUM best_idx = -1 # Find the BEST match vA_co = vA.co for i2, vB in enumerate(vtls_B): # Enforce edge users. if not PREF_EDGE_USERS or edge_users[i1] == edge_users[i2]: flipvec[:] = vB.co flipvec.x = -flipvec.x l = (vA_co - flipvec).length if l < best_len: best_len = l best_idx = i2 if best_idx != -1: pair_ls.append((vtls_A[i1].index, vtls_B[best_idx].index)) # neg, pos. # Now we can merge the weights if PREF_MODE == 0: # Middle newVWeightDict = [vWeightDict[i] for i in xrange(len(me.verts)) ] # Have empty dicts just incase for pair_ls in (mirror_pairs_l2r, mirror_pairs_r2l): if PREF_FLIP_NAMES: for i1, i2 in pair_ls: flipWeight, groupNames = BPyMesh.dictWeightFlipGroups( vWeightDict[i2], groupNames, PREF_CREATE_FLIP_NAMES) newVWeightDict[i1] = BPyMesh.dictWeightMerge( [vWeightDict[i1], flipWeight]) else: for i1, i2 in pair_ls: newVWeightDict[i1] = BPyMesh.dictWeightMerge( [vWeightDict[i1], vWeightDict[i2]]) vWeightDict = newVWeightDict elif PREF_MODE == 1: # Left if PREF_FLIP_NAMES: for i1, i2 in mirror_pairs_l2r: vWeightDict[i2], groupNames = BPyMesh.dictWeightFlipGroups( vWeightDict[i1], groupNames, PREF_CREATE_FLIP_NAMES) else: for i1, i2 in mirror_pairs_l2r: vWeightDict[i2] = vWeightDict[ i1] # Warning Multiple instances of the same data, its ok in this case but dont modify later. elif PREF_MODE == 2: # Right if PREF_FLIP_NAMES: for i1, i2 in mirror_pairs_r2l: vWeightDict[i2], groupNames = BPyMesh.dictWeightFlipGroups( vWeightDict[i1], groupNames, PREF_CREATE_FLIP_NAMES) else: for i1, i2 in mirror_pairs_r2l: vWeightDict[i2] = vWeightDict[i1] # Warning, ditto above BPyMesh.dict2MeshWeight(me, groupNames, vWeightDict) me.update()