| 
									
										
										
										
											2009-11-05 12:37:49 +00:00
										 |  |  | # ##### BEGIN GPL LICENSE BLOCK ##### | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | #  This program is free software; you can redistribute it and/or | 
					
						
							|  |  |  | #  modify it under the terms of the GNU General Public License | 
					
						
							|  |  |  | #  as published by the Free Software Foundation; either version 2 | 
					
						
							|  |  |  | #  of the License, or (at your option) any later version. | 
					
						
							| 
									
										
										
										
											2009-11-21 23:55:14 +00:00
										 |  |  | # | 
					
						
							| 
									
										
										
										
											2009-11-05 12:37:49 +00:00
										 |  |  | #  This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | #  but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  | #  GNU General Public License for more details. | 
					
						
							| 
									
										
										
										
											2009-11-21 23:55:14 +00:00
										 |  |  | # | 
					
						
							| 
									
										
										
										
											2009-11-05 12:37:49 +00:00
										 |  |  | #  You should have received a copy of the GNU General Public License | 
					
						
							|  |  |  | #  along with this program; if not, write to the Free Software Foundation, | 
					
						
							| 
									
										
										
										
											2010-02-12 13:34:04 +00:00
										 |  |  | #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 
					
						
							| 
									
										
										
										
											2009-11-05 12:37:49 +00:00
										 |  |  | # | 
					
						
							|  |  |  | # ##### END GPL LICENSE BLOCK ##### | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-07-25 06:40:16 +00:00
										 |  |  | # <pep8-80 compliant> | 
					
						
							| 
									
										
										
										
											2009-12-13 14:38:30 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-05 12:37:49 +00:00
										 |  |  | import bpy | 
					
						
							| 
									
										
										
										
											2011-08-12 06:57:00 +00:00
										 |  |  | from bpy.types import Operator | 
					
						
							| 
									
										
										
										
											2009-11-05 12:37:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-25 11:07:05 +11:00
										 |  |  | from bpy.props import ( | 
					
						
							|  |  |  |     EnumProperty, | 
					
						
							|  |  |  |     IntProperty, | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2011-04-29 05:32:27 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-31 14:46:28 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-12 06:57:00 +00:00
										 |  |  | class MeshMirrorUV(Operator): | 
					
						
							| 
									
										
										
										
											2012-07-03 09:02:41 +00:00
										 |  |  |     """Copy mirror UV coordinates on the X axis based on a mirrored mesh""" | 
					
						
							| 
									
										
										
										
											2011-04-29 05:32:27 +00:00
										 |  |  |     bl_idname = "mesh.faces_mirror_uv" | 
					
						
							| 
									
										
										
										
											2010-01-13 17:58:26 +00:00
										 |  |  |     bl_label = "Copy Mirrored UV coords" | 
					
						
							| 
									
										
										
										
											2010-03-01 00:03:51 +00:00
										 |  |  |     bl_options = {'REGISTER', 'UNDO'} | 
					
						
							| 
									
										
										
										
											2010-01-31 14:46:28 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-19 19:25:20 +00:00
										 |  |  |     direction = EnumProperty( | 
					
						
							|  |  |  |             name="Axis Direction", | 
					
						
							|  |  |  |             items=(('POSITIVE', "Positive", ""), | 
					
						
							|  |  |  |                    ('NEGATIVE', "Negative", "")), | 
					
						
							|  |  |  |             ) | 
					
						
							| 
									
										
										
										
											2011-04-29 05:32:27 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-05 03:10:29 +00:00
										 |  |  |     precision = IntProperty( | 
					
						
							|  |  |  |             name="Precision", | 
					
						
							|  |  |  |             description=("Tolerance for finding vertex duplicates"), | 
					
						
							|  |  |  |             min=1, max=16, | 
					
						
							|  |  |  |             soft_min=1, soft_max=16, | 
					
						
							|  |  |  |             default=3, | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-08-09 01:37:09 +00:00
										 |  |  |     @classmethod | 
					
						
							|  |  |  |     def poll(cls, context): | 
					
						
							| 
									
										
										
										
											2011-07-01 13:25:00 +00:00
										 |  |  |         obj = context.active_object | 
					
						
							|  |  |  |         return (obj and obj.type == 'MESH' and obj.data.uv_textures.active) | 
					
						
							| 
									
										
										
										
											2010-01-31 14:46:28 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-05 12:37:49 +00:00
										 |  |  |     def execute(self, context): | 
					
						
							| 
									
										
										
										
											2011-04-29 05:32:27 +00:00
										 |  |  |         DIR = (self.direction == 'NEGATIVE') | 
					
						
							| 
									
										
										
										
											2013-06-05 03:10:29 +00:00
										 |  |  |         precision = self.precision | 
					
						
							|  |  |  |         double_warn = 0 | 
					
						
							| 
									
										
										
										
											2010-01-31 14:46:28 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-13 17:58:26 +00:00
										 |  |  |         ob = context.active_object | 
					
						
							|  |  |  |         is_editmode = (ob.mode == 'EDIT') | 
					
						
							|  |  |  |         if is_editmode: | 
					
						
							|  |  |  |             bpy.ops.object.mode_set(mode='OBJECT', toggle=False) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         mesh = ob.data | 
					
						
							| 
									
										
										
										
											2010-01-31 14:46:28 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-13 17:58:26 +00:00
										 |  |  |         # mirror lookups | 
					
						
							|  |  |  |         mirror_gt = {} | 
					
						
							|  |  |  |         mirror_lt = {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-05 03:10:29 +00:00
										 |  |  |         vcos = (v.co.to_tuple(precision) for v in mesh.vertices) | 
					
						
							| 
									
										
										
										
											2010-01-13 17:58:26 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         for i, co in enumerate(vcos): | 
					
						
							| 
									
										
										
										
											2012-06-29 12:41:39 +00:00
										 |  |  |             if co[0] >= 0.0: | 
					
						
							| 
									
										
										
										
											2013-06-05 03:10:29 +00:00
										 |  |  |                 double_warn += co in mirror_gt | 
					
						
							| 
									
										
										
										
											2010-01-13 17:58:26 +00:00
										 |  |  |                 mirror_gt[co] = i | 
					
						
							| 
									
										
										
										
											2012-06-29 12:41:39 +00:00
										 |  |  |             if co[0] <= 0.0: | 
					
						
							| 
									
										
										
										
											2013-06-05 03:10:29 +00:00
										 |  |  |                 double_warn += co in mirror_lt | 
					
						
							| 
									
										
										
										
											2010-01-13 17:58:26 +00:00
										 |  |  |                 mirror_lt[co] = i | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         vmap = {} | 
					
						
							| 
									
										
										
										
											2011-07-25 06:40:16 +00:00
										 |  |  |         for mirror_a, mirror_b in ((mirror_gt, mirror_lt), | 
					
						
							|  |  |  |                                    (mirror_lt, mirror_gt)): | 
					
						
							| 
									
										
										
										
											2010-01-13 17:58:26 +00:00
										 |  |  |             for co, i in mirror_a.items(): | 
					
						
							|  |  |  |                 nco = (-co[0], co[1], co[2]) | 
					
						
							|  |  |  |                 j = mirror_b.get(nco) | 
					
						
							|  |  |  |                 if j is not None: | 
					
						
							|  |  |  |                     vmap[i] = j | 
					
						
							| 
									
										
										
										
											2010-01-31 14:46:28 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-03 17:19:58 +00:00
										 |  |  |         polys = mesh.polygons | 
					
						
							|  |  |  |         loops = mesh.loops | 
					
						
							| 
									
										
										
										
											2012-04-22 23:51:50 +00:00
										 |  |  |         uv_loops = mesh.uv_layers.active.data | 
					
						
							| 
									
										
										
										
											2012-04-03 17:19:58 +00:00
										 |  |  |         nbr_polys = len(polys) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         mirror_pm = {} | 
					
						
							|  |  |  |         pmap = {} | 
					
						
							|  |  |  |         puvs = [None] * nbr_polys | 
					
						
							|  |  |  |         puvs_cpy = [None] * nbr_polys | 
					
						
							|  |  |  |         puvsel = [None] * nbr_polys | 
					
						
							|  |  |  |         pcents = [None] * nbr_polys | 
					
						
							|  |  |  |         vidxs = [None] * nbr_polys | 
					
						
							|  |  |  |         for i, p in enumerate(polys): | 
					
						
							|  |  |  |             lstart = lend = p.loop_start | 
					
						
							|  |  |  |             lend += p.loop_total | 
					
						
							|  |  |  |             puvs[i] = tuple(uv.uv for uv in uv_loops[lstart:lend]) | 
					
						
							|  |  |  |             puvs_cpy[i] = tuple(uv.copy() for uv in puvs[i]) | 
					
						
							|  |  |  |             puvsel[i] = (False not in | 
					
						
							| 
									
										
										
										
											2012-10-08 08:28:05 +00:00
										 |  |  |                          (uv.select for uv in uv_loops[lstart:lend])) | 
					
						
							| 
									
										
										
										
											2012-04-03 17:19:58 +00:00
										 |  |  |             # Vert idx of the poly. | 
					
						
							| 
									
										
										
										
											2012-06-29 12:41:39 +00:00
										 |  |  |             vidxs[i] = tuple(l.vertex_index for l in loops[lstart:lend]) | 
					
						
							| 
									
										
										
										
											2013-06-05 03:10:29 +00:00
										 |  |  |             pcents[i] = p.center | 
					
						
							| 
									
										
										
										
											2012-04-03 17:19:58 +00:00
										 |  |  |             # Preparing next step finding matching polys. | 
					
						
							| 
									
										
										
										
											2012-06-29 12:41:39 +00:00
										 |  |  |             mirror_pm[tuple(sorted(vidxs[i]))] = i | 
					
						
							| 
									
										
										
										
											2012-04-03 17:19:58 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         for i in range(nbr_polys): | 
					
						
							|  |  |  |             # Find matching mirror poly. | 
					
						
							|  |  |  |             tvidxs = [vmap.get(j) for j in vidxs[i]] | 
					
						
							|  |  |  |             if None not in tvidxs: | 
					
						
							|  |  |  |                 tvidxs.sort() | 
					
						
							|  |  |  |                 j = mirror_pm.get(tuple(tvidxs)) | 
					
						
							| 
									
										
										
										
											2010-01-13 17:58:26 +00:00
										 |  |  |                 if j is not None: | 
					
						
							| 
									
										
										
										
											2012-04-03 17:19:58 +00:00
										 |  |  |                     pmap[i] = j | 
					
						
							| 
									
										
										
										
											2010-01-31 14:46:28 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-03 17:19:58 +00:00
										 |  |  |         for i, j in pmap.items(): | 
					
						
							|  |  |  |             if not puvsel[i] or not puvsel[j]: | 
					
						
							| 
									
										
										
										
											2010-01-13 17:58:26 +00:00
										 |  |  |                 continue | 
					
						
							| 
									
										
										
										
											2012-04-03 17:19:58 +00:00
										 |  |  |             elif DIR == 0 and pcents[i][0] < 0.0: | 
					
						
							| 
									
										
										
										
											2010-01-13 17:58:26 +00:00
										 |  |  |                 continue | 
					
						
							| 
									
										
										
										
											2012-04-03 17:19:58 +00:00
										 |  |  |             elif DIR == 1 and pcents[i][0] > 0.0: | 
					
						
							| 
									
										
										
										
											2010-01-13 17:58:26 +00:00
										 |  |  |                 continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # copy UVs | 
					
						
							| 
									
										
										
										
											2012-04-03 17:19:58 +00:00
										 |  |  |             uv1 = puvs[i] | 
					
						
							|  |  |  |             uv2 = puvs_cpy[j] | 
					
						
							| 
									
										
										
										
											2010-01-31 14:46:28 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-13 17:58:26 +00:00
										 |  |  |             # get the correct rotation | 
					
						
							| 
									
										
										
										
											2012-04-03 17:19:58 +00:00
										 |  |  |             v1 = vidxs[j] | 
					
						
							|  |  |  |             v2 = tuple(vmap[k] for k in vidxs[i]) | 
					
						
							| 
									
										
										
										
											2010-01-31 14:46:28 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-06-21 02:16:22 +00:00
										 |  |  |             if len(v1) == len(v2): | 
					
						
							|  |  |  |                 for k in range(len(v1)): | 
					
						
							|  |  |  |                     k_map = v1.index(v2[k]) | 
					
						
							|  |  |  |                     uv1[k].xy = - (uv2[k_map].x - 0.5) + 0.5, uv2[k_map].y | 
					
						
							| 
									
										
										
										
											2010-01-31 14:46:28 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-13 17:58:26 +00:00
										 |  |  |         if is_editmode: | 
					
						
							|  |  |  |             bpy.ops.object.mode_set(mode='EDIT', toggle=False) | 
					
						
							| 
									
										
										
										
											2009-11-05 12:37:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-05 03:10:29 +00:00
										 |  |  |         if double_warn: | 
					
						
							|  |  |  |             self.report({'WARNING'}, | 
					
						
							|  |  |  |                         "%d duplicates found, mirror may be incomplete" % | 
					
						
							|  |  |  |                         double_warn) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-13 17:58:26 +00:00
										 |  |  |         return {'FINISHED'} | 
					
						
							| 
									
										
										
										
											2016-01-08 02:54:15 +11:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class MeshSelectNext(Operator): | 
					
						
							|  |  |  |     """Select the next element (using selection order)""" | 
					
						
							|  |  |  |     bl_idname = "mesh.select_next_item" | 
					
						
							|  |  |  |     bl_label = "Select Next Element" | 
					
						
							|  |  |  |     bl_options = {'REGISTER', 'UNDO'} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def poll(cls, context): | 
					
						
							|  |  |  |         return (context.mode == 'EDIT_MESH') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def execute(self, context): | 
					
						
							|  |  |  |         import bmesh | 
					
						
							|  |  |  |         from .bmesh import find_adjacent | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         obj = context.active_object | 
					
						
							|  |  |  |         me = obj.data | 
					
						
							|  |  |  |         bm = bmesh.from_edit_mesh(me) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if find_adjacent.select_next(bm, self.report): | 
					
						
							|  |  |  |             bm.select_flush_mode() | 
					
						
							|  |  |  |             bmesh.update_edit_mesh(me, False) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return {'FINISHED'} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class MeshSelectPrev(Operator): | 
					
						
							|  |  |  |     """Select the next element (using selection order)""" | 
					
						
							|  |  |  |     bl_idname = "mesh.select_prev_item" | 
					
						
							|  |  |  |     bl_label = "Select Previous Element" | 
					
						
							|  |  |  |     bl_options = {'REGISTER', 'UNDO'} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def poll(cls, context): | 
					
						
							|  |  |  |         return (context.mode == 'EDIT_MESH') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def execute(self, context): | 
					
						
							|  |  |  |         import bmesh | 
					
						
							|  |  |  |         from .bmesh import find_adjacent | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         obj = context.active_object | 
					
						
							|  |  |  |         me = obj.data | 
					
						
							|  |  |  |         bm = bmesh.from_edit_mesh(me) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if find_adjacent.select_prev(bm, self.report): | 
					
						
							|  |  |  |             bm.select_flush_mode() | 
					
						
							|  |  |  |             bmesh.update_edit_mesh(me, False) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return {'FINISHED'} | 
					
						
							| 
									
										
										
										
											2016-11-02 15:11:58 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # XXX This is hackish (going forth and back from Object mode...), to be redone once we have proper support of | 
					
						
							|  |  |  | #     custom normals in BMesh/edit mode. | 
					
						
							|  |  |  | class MehsSetNormalsFromFaces(Operator): | 
					
						
							|  |  |  |     """Set the custom vertex normals from the selected faces ones""" | 
					
						
							|  |  |  |     bl_idname = "mesh.set_normals_from_faces" | 
					
						
							|  |  |  |     bl_label = "Set Normals From Faces" | 
					
						
							|  |  |  |     bl_options = {'REGISTER', 'UNDO'} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def poll(cls, context): | 
					
						
							|  |  |  |         return (context.mode == 'EDIT_MESH' and context.edit_object.data.polygons) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def execute(self, context): | 
					
						
							|  |  |  |         import mathutils | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         bpy.ops.object.mode_set(mode='OBJECT') | 
					
						
							|  |  |  |         obj = context.active_object | 
					
						
							|  |  |  |         me = obj.data | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         v2nors = {} | 
					
						
							|  |  |  |         for p in me.polygons: | 
					
						
							|  |  |  |             if not p.select: | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             for lidx, vidx in zip(p.loop_indices, p.vertices): | 
					
						
							|  |  |  |                 assert(me.loops[lidx].vertex_index == vidx) | 
					
						
							|  |  |  |                 v2nors.setdefault(vidx, []).append(p.normal) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for nors in v2nors.values(): | 
					
						
							|  |  |  |             nors[:] = [sum(nors, mathutils.Vector((0, 0, 0))).normalized()] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if not me.has_custom_normals: | 
					
						
							|  |  |  |             me.create_normals_split() | 
					
						
							|  |  |  |         me.calc_normals_split() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         normals = [] | 
					
						
							|  |  |  |         for l in me.loops: | 
					
						
							|  |  |  |             nor = v2nors.get(l.vertex_index, [None])[0] | 
					
						
							|  |  |  |             if nor is None: | 
					
						
							|  |  |  |                 nor = l.normal | 
					
						
							|  |  |  |             normals.append(nor.to_tuple()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         me.normals_split_custom_set(normals) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         me.free_normals_split() | 
					
						
							|  |  |  |         bpy.ops.object.mode_set(mode='EDIT') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return {'FINISHED'} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-18 20:03:24 +11:00
										 |  |  | 
 | 
					
						
							|  |  |  | classes = ( | 
					
						
							|  |  |  |     MehsSetNormalsFromFaces, | 
					
						
							|  |  |  |     MeshMirrorUV, | 
					
						
							|  |  |  |     MeshSelectNext, | 
					
						
							|  |  |  |     MeshSelectPrev, | 
					
						
							| 
									
										
										
										
											2017-03-25 11:07:05 +11:00
										 |  |  | ) |