mirror of
https://github.com/blender/blender-addons-contrib.git
synced 2025-08-16 16:14:56 +00:00
212 lines
6.1 KiB
Python
212 lines
6.1 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 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": "https://developer.blender.org/maniphest/task/edit/form/2/",
|
|
"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()
|