Skip to content
Snippets Groups Projects
mesh_copy_uvs_from_joined.py 6.04 KiB
Newer Older
  • Learn to ignore specific revisions
  • # ##### 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 compliant>
    
    bl_info = {
        "name": "Copy UV's from Joined",
        "description": "Copy UV coordinates from the active joined mesh",
        "author": "Sergey Sharybin",
        "version": (0, 1),
    
        "blender": (2, 63, 0),
    
        "location": "Object mode 'Make Links' menu",
        "wiki_url": "",
        "tracker_url": "",
        "category": "Object"}
    
    
    import bpy
    from bpy.types import Operator
    from mathutils import Vector
    
    FLT_MAX = 30000.0
    KEY_PRECISION = 1
    
    
    def MINMAX_INIT():
        return (Vector((+FLT_MAX, +FLT_MAX, +FLT_MAX)),
                Vector((-FLT_MAX, -FLT_MAX, -FLT_MAX)))
    
    
    def MINMAX_DO(min, max, vec):
        for x in range(3):
            if vec[x] < min[x]:
                min[x] = vec[x]
    
            if vec[x] > max[x]:
                max[x] = vec[x]
    
    
    def getObjectAABB(obj):
        min, max = MINMAX_INIT()
    
        matrix = obj.matrix_world.copy()
        for vec in obj.bound_box:
            v = matrix * Vector(vec)
            MINMAX_DO(min, max, v)
    
        return min, max
    
    
    class  OBJECT_OT_copy_uv_from_joined(Operator):
        """
        Copy UVs from joined objects into originals
        """
    
        bl_idname = "object.copy_uv_from_joined"
        bl_label = "Copy UVs from Joined"
    
        def _findTranslation(self, obact, objects):
            """
            Find a translation from original objects to joined
            """
    
            bb_joined = getObjectAABB(obact)
            bb_orig = MINMAX_INIT()
    
            for ob in objects:
                if ob != obact:
                    bb = getObjectAABB(ob)
                    MINMAX_DO(bb_orig[0], bb_orig[1], bb[0])
                    MINMAX_DO(bb_orig[0], bb_orig[1], bb[1])
    
            return bb_joined[0] - bb_orig[0]
    
        def _getPolygonMedian(self, me, poly):
            median = Vector()
            verts = me.vertices
    
            for vert_index in poly.vertices:
                median += verts[vert_index].co
    
            median /= len(poly.vertices)
    
            return median
    
        def _getVertexLookupMap(self, obact, objects):
            """
            Create a vertex lookup map from joined object space to original object
            """
    
    
            uv_map = {}
    
            T = self._findTranslation(obact, objects)
    
            for obj in objects:
                if obj != obact:
                    me = obj.data
                    mat = obj.matrix_world.copy()
    
                    uv_layer = me.uv_layers.active
    
                    for poly in me.polygons:
                        center = mat * self._getPolygonMedian(me, poly) + T
                        center_key = center.to_tuple(KEY_PRECISION)
    
                        for loop_index in poly.loop_indices:
                            loop = me.loops[loop_index]
                            vert = me.vertices[loop.vertex_index]
                            vec = mat * vert.co + T
    
                            key = (center_key, vec.to_tuple(KEY_PRECISION))
    
    
                            uv_map.setdefault(key, []).append((center, vec, (uv_layer, loop_index)))
    
            return uv_map
    
    
        def execute(self, context):
            obact = context.object
    
            # Check wether we're working with meshes
            # other object types are not supported
            if obact.type != 'MESH':
                self.report({'ERROR'}, "Only meshes are supported")
                return {'CANCELLED'}
    
            objects = context.selected_objects
    
            for obj in context.selected_objects:
                if obj.type != 'MESH':
                    self.report({'ERROR'}, "Only meshes are supported")
                    return {'CANCELLED'}
    
            uv_map = self._getVertexLookupMap(obact, objects)
    
            me = obact.data
            mat = obact.matrix_world.copy()
            uv_layer = me.uv_layers.active
    
            for poly in me.polygons:
                center = mat * self._getPolygonMedian(me, poly)
                center_key = center.to_tuple(KEY_PRECISION)
    
                for loop_index in poly.loop_indices:
                    loop = me.loops[loop_index]
                    vert = me.vertices[loop.vertex_index]
                    vec = mat * vert.co
    
                    key = (center_key, vec.to_tuple(KEY_PRECISION))
                    check_list = uv_map.get(key)
    
                    if check_list is not None:
                        new_uv = None
                        closest_data = None
    
                        dist = FLT_MAX
                        for x in check_list:
                            cur_center, cur_vec, data = x
    
                            d1 = Vector(cur_center) - Vector(center)
                            d2 = Vector(cur_vec) - Vector(vec)
    
                            d = d1.length_squared + d2.length_squared
    
                            if d < dist:
                                closest_data = data
                                dist = d
    
                        if closest_data is not None:
                            orig_uv_layer, orig_loop_index = closest_data
                            new_uv = uv_layer.data[loop_index].uv
                            orig_uv_layer.data[orig_loop_index].uv = new_uv
                    else:
    
                        print("Failed to lookup %r" % (key,))
    
    
            return {'FINISHED'}
    
    
    def menu_func(self, context):
        self.layout.operator("OBJECT_OT_copy_uv_from_joined",
                             text="Join as UVs (active to other selected)",
                             icon="PLUGIN")
    
    
    def register():
        bpy.utils.register_module(__name__)
        
        bpy.types.VIEW3D_MT_make_links.append(menu_func)
        
    
    def unregister():
        bpy.utils.unregister_module(__name__)
    
        bpy.types.VIEW3D_MT_make_links.remove(menu_func)
    
    
    if __name__ == "__main__":
        register()