import bpy import bmesh import operator from mathutils import Vector from collections import defaultdict from math import pi from . import settings frame_range = 50 class op(bpy.types.Operator): bl_idname = "uv.textools_bake_explode" bl_label = "Explode" bl_description = "Explode selected bake pairs with animation keyframes" bl_options = {'REGISTER', 'UNDO'} @classmethod def poll(cls, context): if len(settings.sets) <= 1: return False return True def execute(self, context): explode(self) return {'FINISHED'} def explode(self): sets = settings.sets set_bounds = {} set_volume = {} avg_side = 0 for set in sets: set_bounds[set] = get_bbox_set(set) set_volume[set] = set_bounds[set]['size'].x * set_bounds[set]['size'].y * set_bounds[set]['size'].z avg_side+=set_bounds[set]['size'].x avg_side+=set_bounds[set]['size'].y avg_side+=set_bounds[set]['size'].z avg_side/=(len(sets)*3) sorted_set_volume = sorted(set_volume.items(), key=operator.itemgetter(1)) sorted_sets = [item[0] for item in sorted_set_volume] sorted_sets.reverse() # All combined bounding boxes bbox_all = merge_bounds(list(set_bounds.values())) bbox_max = set_bounds[ sorted_sets[0] ] # max_bbox(list(set_bounds.values())) # Offset sets into their direction dir_offset_last_bbox = {} for i in range(0,6): dir_offset_last_bbox[i] = bbox_max #bbox_all bpy.context.scene.frame_start = 0 bpy.context.scene.frame_end = frame_range bpy.context.scene.frame_current = 0 # Process each set for set in sorted_sets: if set_bounds[set] != bbox_max: delta = set_bounds[set]['center'] - bbox_all['center'] offset_set(set, delta, avg_side*0.35, dir_offset_last_bbox ) def offset_set(set, delta, margin, dir_offset_last_bbox): objects = set.objects_low + set.objects_high + set.objects_cage # print("\nSet '{}' with {}x".format(set.name, len(objects) )) # Which Direction? delta_max = max(abs(delta.x), abs(delta.y), abs(delta.z)) direction = [0,0,0] if delta_max > 0: for i in range(0,3): if abs(delta[i]) == delta_max: direction[i] = delta[i]/abs(delta[i]) else: direction[i] = 0 else: # Default when not delta offset was measure move up direction = [0,0,1] delta = Vector((direction[0], direction[1], direction[2])) # Get Key key = get_delta_key(delta) # Calculate Offset bbox = get_bbox_set(set) bbox_last = dir_offset_last_bbox[key] offset = Vector((0,0,0)) if delta.x == 1: offset = delta * ( bbox_last['max'].x - bbox['min'].x ) elif delta.x == -1: offset = delta * -( bbox_last['min'].x - bbox['max'].x ) elif delta.y == 1: offset = delta * ( bbox_last['max'].y - bbox['min'].y ) elif delta.y == -1: offset = delta * -( bbox_last['min'].y - bbox['max'].y ) elif delta.z == 1: offset = delta * ( bbox_last['max'].z - bbox['min'].z ) elif delta.z == -1: offset = delta * -( bbox_last['min'].z - bbox['max'].z ) # Add margin offset+= delta * margin # Offset items # https://blenderartists.org/forum/showthread.php?237761-Blender-2-6-Set-keyframes-using-Python-script # http://blenderscripting.blogspot.com.au/2011/05/inspired-by-post-on-ba-it-just-so.html # Set key A bpy.context.scene.frame_current = 0 for obj in objects: obj.keyframe_insert(data_path="location") for obj in objects: obj.location += offset bpy.context.view_layer.update() # Set key B bpy.context.scene.frame_current = frame_range for obj in objects: obj.keyframe_insert(data_path="location") # Update last bbox in direction dir_offset_last_bbox[key] = get_bbox_set(set) def get_delta_key(delta): # print("Get key {} is: {}".format(delta, delta.y == -1 )) if delta.x == -1: return 0 elif delta.x == 1: return 1 if delta.y == -1: return 2 elif delta.y == 1: return 3 if delta.z == -1: return 4 elif delta.z == 1: return 5 def merge_bounds(bounds): box_min = bounds[0]['min'].copy() box_max = bounds[0]['max'].copy() for bbox in bounds: # box_min.x = -8 box_min.x = min(box_min.x, bbox['min'].x) box_min.y = min(box_min.y, bbox['min'].y) box_min.z = min(box_min.z, bbox['min'].z) box_max.x = max(box_max.x, bbox['max'].x) box_max.y = max(box_max.y, bbox['max'].y) box_max.z = max(box_max.z, bbox['max'].z) return { 'min':box_min, 'max':box_max, 'size':(box_max-box_min), 'center':box_min+(box_max-box_min)/2 } def get_bbox_set(set): objects = set.objects_low + set.objects_high + set.objects_cage bounds = [] for obj in objects: bounds.append( get_bbox(obj) ) return merge_bounds(bounds) def get_bbox(obj): corners = [obj.matrix_world @ Vector(corner) for corner in obj.bound_box] # Get world space Min / Max box_min = Vector((corners[0].x, corners[0].y, corners[0].z)) box_max = Vector((corners[0].x, corners[0].y, corners[0].z)) for corner in corners: # box_min.x = -8 box_min.x = min(box_min.x, corner.x) box_min.y = min(box_min.y, corner.y) box_min.z = min(box_min.z, corner.z) box_max.x = max(box_max.x, corner.x) box_max.y = max(box_max.y, corner.y) box_max.z = max(box_max.z, corner.z) return { 'min':box_min, 'max':box_max, 'size':(box_max-box_min), 'center':box_min+(box_max-box_min)/2 } bpy.utils.register_class(op)