forked from bartvdbraak/blender
139 lines
4.6 KiB
Python
139 lines
4.6 KiB
Python
# ##### 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.
|
|
#
|
|
# 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.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software Foundation,
|
|
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
#
|
|
# ##### END GPL LICENSE BLOCK #####
|
|
|
|
# <pep8-80 compliant>
|
|
|
|
import bpy
|
|
from bpy.types import Operator
|
|
|
|
from bpy.props import EnumProperty
|
|
|
|
|
|
class MeshMirrorUV(Operator):
|
|
"""Copy mirror UV coordinates on the X axis based on a mirrored mesh"""
|
|
bl_idname = "mesh.faces_mirror_uv"
|
|
bl_label = "Copy Mirrored UV coords"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
direction = EnumProperty(
|
|
name="Axis Direction",
|
|
items=(('POSITIVE', "Positive", ""),
|
|
('NEGATIVE', "Negative", "")),
|
|
)
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
obj = context.active_object
|
|
return (obj and obj.type == 'MESH' and obj.data.uv_textures.active)
|
|
|
|
def execute(self, context):
|
|
DIR = (self.direction == 'NEGATIVE')
|
|
|
|
ob = context.active_object
|
|
is_editmode = (ob.mode == 'EDIT')
|
|
if is_editmode:
|
|
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
|
|
|
|
mesh = ob.data
|
|
|
|
# mirror lookups
|
|
mirror_gt = {}
|
|
mirror_lt = {}
|
|
|
|
vcos = (v.co.to_tuple(5) for v in mesh.vertices)
|
|
|
|
for i, co in enumerate(vcos):
|
|
if co[0] >= 0.0:
|
|
mirror_gt[co] = i
|
|
if co[0] <= 0.0:
|
|
mirror_lt[co] = i
|
|
|
|
#for i, v in enumerate(mesh.vertices):
|
|
vmap = {}
|
|
for mirror_a, mirror_b in ((mirror_gt, mirror_lt),
|
|
(mirror_lt, mirror_gt)):
|
|
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
|
|
|
|
polys = mesh.polygons
|
|
loops = mesh.loops
|
|
verts = mesh.vertices
|
|
uv_loops = mesh.uv_layers.active.data
|
|
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
|
|
(uv.select for uv in uv_loops[lstart:lend]))
|
|
# Vert idx of the poly.
|
|
vidxs[i] = tuple(l.vertex_index for l in loops[lstart:lend])
|
|
# As we have no poly.center yet...
|
|
pcents[i] = tuple(map(lambda x: x / p.loop_total,
|
|
map(sum, zip(*(verts[idx].co
|
|
for idx in vidxs[i])))))
|
|
# Preparing next step finding matching polys.
|
|
mirror_pm[tuple(sorted(vidxs[i]))] = i
|
|
|
|
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))
|
|
if j is not None:
|
|
pmap[i] = j
|
|
|
|
for i, j in pmap.items():
|
|
if not puvsel[i] or not puvsel[j]:
|
|
continue
|
|
elif DIR == 0 and pcents[i][0] < 0.0:
|
|
continue
|
|
elif DIR == 1 and pcents[i][0] > 0.0:
|
|
continue
|
|
|
|
# copy UVs
|
|
uv1 = puvs[i]
|
|
uv2 = puvs_cpy[j]
|
|
|
|
# get the correct rotation
|
|
v1 = vidxs[j]
|
|
v2 = tuple(vmap[k] for k in vidxs[i])
|
|
|
|
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
|
|
|
|
if is_editmode:
|
|
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
|
|
|
|
return {'FINISHED'}
|