PEP8 compliance

pull/1/head
Andrew Cassidy 4 years ago
parent f5252922b9
commit f60b85b477
No known key found for this signature in database
GPG Key ID: 963017B38FD477A1

@ -31,7 +31,8 @@ class op(bpy.types.Operator):
# Requires UV map
if not bpy.context.object.data.uv_layers:
return False #self.report({'WARNING'}, "Object must have more than one UV map")
# self.report({'WARNING'}, "Object must have more than one UV map")
return False
# Not in Synced mode
if bpy.context.scene.tool_settings.use_uv_select_sync:
@ -39,16 +40,12 @@ class op(bpy.types.Operator):
return True
def execute(self, context):
align(context, self.direction)
return {'FINISHED'}
def align(context, direction):
# Store selection
utilities_uv.selection_store()
@ -58,8 +55,8 @@ def align(context, direction):
# B-Mesh
obj = bpy.context.active_object
bm = bmesh.from_edit_mesh(obj.data);
uv_layers = bm.loops.layers.uv.verify();
bm = bmesh.from_edit_mesh(obj.data)
uv_layers = bm.loops.layers.uv.verify()
if len(obj.data.uv_layers) == 0:
print("There is no UV channel or UV data set")
@ -98,7 +95,6 @@ def align(context, direction):
else:
print("Unkown direction: "+str(direction))
elif mode == 'EDGE' or mode == 'VERTEX':
print("____ Align Verts")
@ -117,15 +113,10 @@ def align(context, direction):
elif direction == "right":
luv.uv[0] = boundsAll['max'].x
bmesh.update_edit_mesh(obj.data)
# Restore selection
utilities_uv.selection_restore()
bpy.utils.register_class(op)
bpy.utils.register_class(op)

@ -32,10 +32,12 @@ modes={
if hasattr(bpy.types, "ShaderNodeBevel"):
# Has newer bevel shader (2.7 nightly build series)
modes['bevel_mask'] = ub.BakeMode('bake_bevel_mask', type='EMIT', color=(0, 0, 0, 1), params=["bake_bevel_samples","bake_bevel_size"])
modes['normal_tangent_bevel'] = ub.BakeMode('bake_bevel_normal', type='NORMAL', color=(0.5, 0.5, 1, 1), params=["bake_bevel_samples","bake_bevel_size"])
modes['normal_object_bevel'] = ub.BakeMode('bake_bevel_normal', type='NORMAL', color=(0.5, 0.5, 1, 1), normal_space='OBJECT', params=["bake_bevel_samples","bake_bevel_size"])
modes['bevel_mask'] = ub.BakeMode('bake_bevel_mask', type='EMIT', color=(
0, 0, 0, 1), params=["bake_bevel_samples", "bake_bevel_size"])
modes['normal_tangent_bevel'] = ub.BakeMode('bake_bevel_normal', type='NORMAL', color=(
0.5, 0.5, 1, 1), params=["bake_bevel_samples", "bake_bevel_size"])
modes['normal_object_bevel'] = ub.BakeMode('bake_bevel_normal', type='NORMAL', color=(
0.5, 0.5, 1, 1), normal_space='OBJECT', params=["bake_bevel_samples", "bake_bevel_size"])
class op(bpy.types.Operator):
@ -53,7 +55,8 @@ class op(bpy.types.Operator):
bake_mode = utilities_ui.get_bake_mode()
if bake_mode not in modes:
self.report({'ERROR_INVALID_INPUT'}, "Uknown mode '{}' only available: '{}'".format(bake_mode, ", ".join(modes.keys() )) )
self.report({'ERROR_INVALID_INPUT'}, "Uknown mode '{}' only available: '{}'".format(
bake_mode, ", ".join(modes.keys())))
return
# Store Selection
@ -68,7 +71,8 @@ class op(bpy.types.Operator):
size=bpy.context.scene.texToolsSettings.size,
bake_single=bpy.context.scene.texToolsSettings.bake_force_single,
sampling_scale = int(bpy.context.scene.texToolsSettings.bake_sampling),
sampling_scale=int(
bpy.context.scene.texToolsSettings.bake_sampling),
samples=bpy.context.scene.texToolsSettings.bake_samples,
ray_distance=bpy.context.scene.texToolsSettings.bake_ray_distance
)
@ -84,12 +88,12 @@ class op(bpy.types.Operator):
return {'FINISHED'}
def bake(self, mode, size, bake_single, sampling_scale, samples, ray_distance):
print("Bake '{}'".format(mode))
bpy.context.scene.render.engine = modes[mode].engine #Switch render engine
# Switch render engine
bpy.context.scene.render.engine = modes[mode].engine
# Disable edit mode
if bpy.context.view_layer.objects.active != None and bpy.context.object.mode != 'OBJECT':
@ -109,23 +113,27 @@ def bake(self, mode, size, bake_single, sampling_scale, samples, ray_distance):
# Get image name
name_texture = "{}_{}".format(set.name, mode)
if bake_single:
name_texture = "{}_{}".format(sets[0].name, mode)# In Single mode bake into same texture
# In Single mode bake into same texture
name_texture = "{}_{}".format(sets[0].name, mode)
path = bpy.path.abspath("//{}.tga".format(name_texture))
# Requires 1+ low poly objects
if len(set.objects_low) == 0:
self.report({'ERROR_INVALID_INPUT'}, "No low poly object as part of the '{}' set".format(set.name) )
self.report({'ERROR_INVALID_INPUT'},
"No low poly object as part of the '{}' set".format(set.name))
return
# Check for UV maps
for obj in set.objects_low:
if not obj.data.uv_layers or len(obj.data.uv_layers) == 0:
self.report({'ERROR_INVALID_INPUT'}, "No UV map available for '{}'".format(obj.name))
self.report({'ERROR_INVALID_INPUT'},
"No UV map available for '{}'".format(obj.name))
return
# Check for cage inconsistencies
if len(set.objects_cage) > 0 and (len(set.objects_low) != len(set.objects_cage)):
self.report({'ERROR_INVALID_INPUT'}, "{}x cage objects do not match {}x low poly objects for '{}'".format(len(set.objects_cage), len(set.objects_low), obj.name))
self.report({'ERROR_INVALID_INPUT'}, "{}x cage objects do not match {}x low poly objects for '{}'".format(
len(set.objects_cage), len(set.objects_low), obj.name))
return
# Get Materials
@ -136,7 +144,6 @@ def bake(self, mode, size, bake_single, sampling_scale, samples, ray_distance):
else:
material_empty = bpy.data.materials.new(name="TT_bake_node")
# Assign Materials to Objects
if (len(set.objects_high) + len(set.objects_float)) == 0:
# Low poly bake: Assign material to lowpoly
@ -153,15 +160,14 @@ def bake(self, mode, size, bake_single, sampling_scale, samples, ray_distance):
assign_vertex_color(mode, obj)
assign_material(mode, obj, material_loaded)
# Setup Image
is_clear = (not bake_single) or (bake_single and s == 0)
image = setup_image(mode, name_texture, render_width, render_height, path, is_clear)
image = setup_image(mode, name_texture, render_width,
render_height, path, is_clear)
# Assign bake node to Material
setup_image_bake_node(set.objects_low[0], image)
print("Bake '{}' = {}".format(set.name, path))
# Hide all cage objects i nrender
@ -171,7 +177,8 @@ def bake(self, mode, size, bake_single, sampling_scale, samples, ray_distance):
# Bake each low poly object in this set
for i in range(len(set.objects_low)):
obj_low = set.objects_low[i]
obj_cage = None if i >= len(set.objects_cage) else set.objects_cage[i]
obj_cage = None if i >= len(
set.objects_cage) else set.objects_cage[i]
# Disable hide render
obj_low.hide_render = False
@ -190,7 +197,6 @@ def bake(self, mode, size, bake_single, sampling_scale, samples, ray_distance):
area.spaces[0].image = image
# bpy.data.screens['UV Editing'].areas[1].spaces[0].image = image
bpy.ops.object.mode_set(mode='OBJECT')
for obj_high in (set.objects_high):
@ -231,7 +237,6 @@ def bake(self, mode, size, bake_single, sampling_scale, samples, ray_distance):
for obj_cage in set.objects_cage:
obj_cage.hide_render = False
# Downsample image?
if not bake_single or (bake_single and s == len(sets)-1):
# When baking single, only downsample on last bake
@ -240,7 +245,8 @@ def bake(self, mode, size, bake_single, sampling_scale, samples, ray_distance):
# Apply composite nodes on final image result
if modes[mode].composite:
apply_composite(image, modes[mode].composite, bpy.context.scene.texToolsSettings.bake_curvature_size)
apply_composite(
image, modes[mode].composite, bpy.context.scene.texToolsSettings.bake_curvature_size)
# image.save()
@ -248,8 +254,6 @@ def bake(self, mode, size, bake_single, sampling_scale, samples, ray_distance):
ub.restore_materials()
def apply_composite(image, scene_name, size):
previous_scene = bpy.context.window.scene
@ -258,8 +262,10 @@ def apply_composite(image, scene_name, size):
if scene_name in bpy.data.scenes:
scene = bpy.data.scenes[scene_name]
else:
path = os.path.join(os.path.dirname(__file__), "resources/compositing.blend")+"\\Scene\\"
bpy.ops.wm.append(filename=scene_name, directory=path, link=False, autoselect=False)
path = os.path.join(os.path.dirname(__file__),
"resources/compositing.blend")+"\\Scene\\"
bpy.ops.wm.append(filename=scene_name, directory=path,
link=False, autoselect=False)
scene = bpy.data.scenes[scene_name]
if scene:
@ -272,12 +278,12 @@ def apply_composite(image, scene_name, size):
if "Offset" in scene.node_tree.nodes:
scene.node_tree.nodes["Offset"].outputs[0].default_value = size
print("Assign offset: {}".format(scene.node_tree.nodes["Offset"].outputs[0].default_value))
print("Assign offset: {}".format(
scene.node_tree.nodes["Offset"].outputs[0].default_value))
# Render image
bpy.ops.render.render(use_viewport=False)
# Get last images of viewer node and render result
image_viewer_node = get_last_item("Viewer Node", bpy.data.images)
image_render_result = get_last_item("Render Result", bpy.data.images)
@ -298,7 +304,6 @@ def apply_composite(image, scene_name, size):
bpy.data.scenes.remove(scene)
def get_last_item(key_name, collection):
# bpy.data.images
# Get last image of a series, e.g. .001, .002, 003
@ -315,8 +320,6 @@ def get_last_item(key_name, collection):
return None
def setup_image(mode, name, width, height, path, is_clear):
image = None
@ -332,7 +335,6 @@ def setup_image(mode, name, width, height, path, is_clear):
# if bpy.data.images[name].has_data == False:
# Previous image does not have data, remove first
# print("Image pointer exists but no data "+name)
# image = bpy.data.images[name]
@ -343,13 +345,13 @@ def setup_image(mode, name, width, height, path, is_clear):
if name not in bpy.data.images:
# Create new image with 32 bit float
is_float_32 = bpy.context.preferences.addons["textools"].preferences.bake_32bit_float == '32'
image = bpy.data.images.new(name, width=width, height=height, float_buffer=is_float_32)
image = bpy.data.images.new(
name, width=width, height=height, float_buffer=is_float_32)
if "_normal_" in image.name:
image.colorspace_settings.name = 'Non-Color'
else:
image.colorspace_settings.name = 'sRGB'
else:
# Reuse existing Image
image = bpy.data.images[name]
@ -364,7 +366,6 @@ def setup_image(mode, name, width, height, path, is_clear):
image.generated_color = modes[mode].color
image.generated_type = 'BLANK'
image.file_format = 'TARGA'
# TODO: Verify that the path exists
@ -373,7 +374,6 @@ def setup_image(mode, name, width, height, path, is_clear):
return image
def setup_image_bake_node(obj, image):
if len(obj.data.materials) <= 0:
@ -397,13 +397,11 @@ def setup_image_bake_node(obj, image):
tree.nodes.active = node
def assign_vertex_color(mode, obj):
if modes[mode].setVColor:
modes[mode].setVColor(obj)
def assign_material(mode, obj, material_bake=None, material_empty=None):
ub.store_materials(obj)
@ -412,31 +410,32 @@ def assign_material(mode, obj, material_bake=None, material_empty=None):
# Select All faces
bpy.ops.object.mode_set(mode='EDIT')
bm = bmesh.from_edit_mesh(bpy.context.active_object.data);
bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
faces = [face for face in bm.faces if face.select]
bpy.ops.mesh.select_all(action='SELECT')
if material_bake:
# Setup properties of bake materials
if mode == 'wireframe':
if "Value" in material_bake.node_tree.nodes:
material_bake.node_tree.nodes["Value"].outputs[0].default_value = bpy.context.scene.texToolsSettings.bake_wireframe_size
material_bake.node_tree.nodes["Value"].outputs[
0].default_value = bpy.context.scene.texToolsSettings.bake_wireframe_size
if mode == 'bevel_mask':
if "Bevel" in material_bake.node_tree.nodes:
material_bake.node_tree.nodes["Bevel"].inputs[0].default_value = bpy.context.scene.texToolsSettings.bake_bevel_size
material_bake.node_tree.nodes["Bevel"].inputs[
0].default_value = bpy.context.scene.texToolsSettings.bake_bevel_size
material_bake.node_tree.nodes["Bevel"].samples = bpy.context.scene.texToolsSettings.bake_bevel_samples
if mode == 'normal_tangent_bevel':
if "Bevel" in material_bake.node_tree.nodes:
material_bake.node_tree.nodes["Bevel"].inputs[0].default_value = bpy.context.scene.texToolsSettings.bake_bevel_size
material_bake.node_tree.nodes["Bevel"].inputs[
0].default_value = bpy.context.scene.texToolsSettings.bake_bevel_size
material_bake.node_tree.nodes["Bevel"].samples = bpy.context.scene.texToolsSettings.bake_bevel_samples
if mode == 'normal_object_bevel':
if "Bevel" in material_bake.node_tree.nodes:
material_bake.node_tree.nodes["Bevel"].inputs[0].default_value = bpy.context.scene.texToolsSettings.bake_bevel_size
material_bake.node_tree.nodes["Bevel"].inputs[
0].default_value = bpy.context.scene.texToolsSettings.bake_bevel_size
material_bake.node_tree.nodes["Bevel"].samples = bpy.context.scene.texToolsSettings.bake_bevel_samples
# Don't apply in diffuse mode
if mode != 'diffuse':
if material_bake:
@ -467,38 +466,31 @@ def assign_material(mode, obj, material_bake=None, material_empty=None):
bpy.ops.object.mode_set(mode='OBJECT')
def get_material(mode):
if modes[mode].material == "":
return None # No material setup requires
# Find or load material
name = modes[mode].material
path = os.path.join(os.path.dirname(__file__), "resources/materials.blend")+"\\Material\\"
path = os.path.join(os.path.dirname(__file__),
"resources/materials.blend")+"\\Material\\"
if "bevel" in mode:
path = os.path.join(os.path.dirname(__file__), "resources/materials_2.80.blend")+"\\Material\\"
path = os.path.join(os.path.dirname(__file__),
"resources/materials_2.80.blend")+"\\Material\\"
print("Get mat {}\n{}".format(mode, path))
if bpy.data.materials.get(name) is None:
print("Material not yet loaded: "+mode)
bpy.ops.wm.append(filename=name, directory=path, link=False, autoselect=False)
bpy.ops.wm.append(filename=name, directory=path,
link=False, autoselect=False)
return bpy.data.materials.get(name)
def cycles_bake(mode, padding, sampling_scale, samples, ray_distance, is_multi, obj_cage):
# if modes[mode].engine == 'BLENDER_EEVEE':
# # Snippet: https://gist.github.com/AndrewRayCode/760c4634a77551827de41ed67585064b
# bpy.context.scene.render.bake_margin = padding
@ -518,7 +510,6 @@ def cycles_bake(mode, padding, sampling_scale, samples, ray_distance, is_multi,
# bpy.ops.object.bake_image()
if modes[mode].engine == 'CYCLES' or modes[mode].engine == 'BLENDER_EEVEE':
if modes[mode].normal_space == 'OBJECT':
@ -578,4 +569,15 @@ def cycles_bake(mode, padding, sampling_scale, samples, ray_distance, is_multi,
cage_object=obj_cage.name
)
bpy.utils.register_class(op)
use_selected_to_active=is_multi,
normal_space=modes[mode].normal_space,
# Use Cage and assign object
use_cage=True,
cage_object=obj_cage.name
)
bpy.utils.register_class(op)

@ -23,15 +23,12 @@ class op(bpy.types.Operator):
return True
def execute(self, context):
explode(self)
return {'FINISHED'}
def explode(self):
sets = settings.sets
@ -40,7 +37,8 @@ def explode(self):
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
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
@ -54,19 +52,18 @@ def explode(self):
# 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()))
# max_bbox(list(set_bounds.values()))
bbox_max = set_bounds[sorted_sets[0]]
# 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:
@ -74,8 +71,6 @@ def explode(self):
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) ))
@ -144,8 +139,6 @@ def offset_set(set, delta, margin, dir_offset_last_bbox):
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:
@ -162,7 +155,6 @@ def get_delta_key(delta):
return 5
def merge_bounds(bounds):
box_min = bounds[0]['min'].copy()
box_max = bounds[0]['max'].copy()
@ -185,7 +177,6 @@ def merge_bounds(bounds):
}
def get_bbox_set(set):
objects = set.objects_low + set.objects_high + set.objects_cage
bounds = []
@ -194,7 +185,6 @@ def get_bbox_set(set):
return merge_bounds(bounds)
def get_bbox(obj):
corners = [obj.matrix_world @ Vector(corner) for corner in obj.bound_box]
@ -218,4 +208,5 @@ def get_bbox(obj):
'center': box_min+(box_max-box_min)/2
}
bpy.utils.register_class(op)

@ -7,25 +7,25 @@ from math import pi
from . import utilities_color
class op(bpy.types.Operator):
bl_idname = "uv.textools_color_from_directions"
bl_label = "Color Directions"
bl_description = "Assign a color ID to different face directions"
bl_options = {'REGISTER', 'UNDO'}
directions : bpy.props.EnumProperty(items=
[('2', '2', 'Top & Bottom, Sides'),
directions: bpy.props.EnumProperty(items=[('2', '2', 'Top & Bottom, Sides'),
('3', '3', 'Top & Bottom, Left & Right, Front & Back'),
('4', '4', 'Top, Left & Right, Front & Back, Bottom'),
('6', '6', 'All sides')],
name="Directions",
default='3'
)
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self)
# def draw(self, context):
# layout = self.layout
# layout.prop(self, "directions")
@ -55,7 +55,6 @@ class op(bpy.types.Operator):
return {'FINISHED'}
def color_elements(self, context):
obj = bpy.context.active_object
@ -65,8 +64,7 @@ def color_elements(self, context):
bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE')
# Collect groups
bm = bmesh.from_edit_mesh(bpy.context.active_object.data);
bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
face_directions = {
'top': [],
@ -77,10 +75,8 @@ def color_elements(self, context):
'back': []
}
print("Directions {}".format(self.directions))
for face in bm.faces:
print("face {} n: {}".format(face.index, face.normal))
# Find dominant direction
@ -114,7 +110,8 @@ def color_elements(self, context):
if self.directions == '2':
groups.append(face_directions['top']+face_directions['bottom'])
groups.append(face_directions['left']+face_directions['right']+face_directions['front']+face_directions['back'])
groups.append(face_directions['left']+face_directions['right'] +
face_directions['front']+face_directions['back'])
if self.directions == '3':
groups.append(face_directions['top']+face_directions['bottom'])
groups.append(face_directions['left']+face_directions['right'])
@ -136,7 +133,7 @@ def color_elements(self, context):
index_color = 0
for group in groups:
# # rebuild bmesh data (e.g. left edit mode previous loop)
bm = bmesh.from_edit_mesh(bpy.context.active_object.data);
bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
if hasattr(bm.faces, "ensure_lookup_table"):
bm.faces.ensure_lookup_table()
@ -148,7 +145,8 @@ def color_elements(self, context):
# Assign to selection
bpy.ops.uv.textools_color_assign(index=index_color)
index_color = (index_color+1) % bpy.context.scene.texToolsSettings.color_ID_count
index_color = (
index_color+1) % bpy.context.scene.texToolsSettings.color_ID_count
bpy.ops.object.mode_set(mode='OBJECT')
utilities_color.validate_face_colors(obj)
@ -203,4 +201,5 @@ def color_elements(self, context):
utilities_color.validate_face_colors(obj)
'''
bpy.utils.register_class(op)

@ -7,13 +7,13 @@ from math import pi
from . import utilities_color
class op(bpy.types.Operator):
bl_idname = "uv.textools_color_from_materials"
bl_label = "Color Elements"
bl_description = "Assign a color ID to each mesh material slot"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
if not bpy.context.active_object:
@ -39,7 +39,6 @@ class op(bpy.types.Operator):
return {'FINISHED'}
def color_materials(self, context):
obj = bpy.context.active_object
@ -50,4 +49,5 @@ def color_materials(self, context):
utilities_color.validate_face_colors(obj)
bpy.utils.register_class(op)

@ -27,16 +27,17 @@ class op(bpy.types.Operator):
return {'FINISHED'}
def export_colors(self, context):
hex_colors = []
for i in range(bpy.context.scene.texToolsSettings.color_ID_count):
color = getattr(bpy.context.scene.texToolsSettings, "color_ID_color_{}".format(i))
color = getattr(bpy.context.scene.texToolsSettings,
"color_ID_color_{}".format(i))
hex_colors.append(utilities_color.color_to_hex(color))
bpy.context.window_manager.clipboard = ", ".join(hex_colors)
bpy.ops.ui.textools_popup('INVOKE_DEFAULT', message="{}x colors copied to clipboard".format(len(hex_colors)))
bpy.ops.ui.textools_popup(
'INVOKE_DEFAULT', message="{}x colors copied to clipboard".format(len(hex_colors)))
bpy.utils.register_class(op)

@ -7,6 +7,7 @@ from math import pi
from . import utilities_color
class op(bpy.types.Operator):
bl_idname = "uv.textools_color_select"
bl_label = "Assign Color"
@ -41,7 +42,6 @@ class op(bpy.types.Operator):
return {'FINISHED'}
def select_color(self, context, index):
print("Color select "+str(index))
@ -49,21 +49,24 @@ def select_color(self, context, index):
# Check for missing slots, materials,..
if index >= len(obj.material_slots):
self.report({'ERROR_INVALID_INPUT'}, "No material slot for color '{}' found".format(index) )
self.report({'ERROR_INVALID_INPUT'},
"No material slot for color '{}' found".format(index))
return
if not obj.material_slots[index].material:
self.report({'ERROR_INVALID_INPUT'}, "No material found for material slot '{}'".format(index) )
self.report({'ERROR_INVALID_INPUT'},
"No material found for material slot '{}'".format(index))
return
if bpy.context.active_object.mode != 'EDIT':
bpy.ops.object.mode_set(mode='EDIT')
# Select faces
bm = bmesh.from_edit_mesh(bpy.context.active_object.data);
bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
bpy.ops.mesh.select_all(action='DESELECT')
for face in bm.faces:
if face.material_index == index:
face.select = True
bpy.utils.register_class(op)

@ -11,8 +11,6 @@ from itertools import chain # 'flattens' collection of iterables
from . import utilities_uv
class op(bpy.types.Operator):
bl_idname = "uv.textools_edge_split_bevel"
bl_label = "Split Bevel"
@ -27,7 +25,6 @@ class op(bpy.types.Operator):
max=0.35
)
@classmethod
def poll(cls, context):
if not bpy.context.active_object:
@ -50,13 +47,11 @@ class op(bpy.types.Operator):
return True
def execute(self, context):
main(self, self.radius)
return {'FINISHED'}
def main(self, radius):
# Store selection
@ -64,14 +59,12 @@ def main(self, radius):
print("____________\nedge split UV sharp edges {}".format(radius))
obj = bpy.context.object
bm = bmesh.from_edit_mesh(bpy.context.active_object.data);
uv_layers = bm.loops.layers.uv.verify();
bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
uv_layers = bm.loops.layers.uv.verify()
islands = utilities_uv.getSelectionIslands()
# Collect UV to Vert
vert_to_uv = utilities_uv.get_vert_to_uv(bm, uv_layers)
uv_to_vert = utilities_uv.get_uv_to_vert(bm, uv_layers)
@ -90,13 +83,10 @@ def main(self, radius):
# Get left and right faces
edge_face_pairs = get_edge_face_pairs(edges)
print("Vert rails: {}x".format(len(vert_rails)))
# for vert in vert_rails:
# print(".. v.idx {} = {}x".format(vert.index, len(vert_rails[vert]) ))
vert_processed = []
vert_uv_pos = []
@ -111,46 +101,48 @@ def main(self, radius):
# v0
if v0 not in vert_processed:
vert_processed.append(v0)
faces, origin, delta = slide_uvs(v0, edge, f0, edges, vert_rails, vert_to_uv)
vert_uv_pos.append( {"v":v0, "f":f0, "origin":origin, "delta":delta, "faces":faces} )
faces, origin, delta = slide_uvs(
v0, edge, f0, edges, vert_rails, vert_to_uv)
vert_uv_pos.append(
{"v": v0, "f": f0, "origin": origin, "delta": delta, "faces": faces})
faces, origin, delta = slide_uvs(v0, edge, f1, edges, vert_rails, vert_to_uv)
vert_uv_pos.append( {"v":v0, "f":f1, "origin":origin, "delta":delta, "faces":faces} )
faces, origin, delta = slide_uvs(
v0, edge, f1, edges, vert_rails, vert_to_uv)
vert_uv_pos.append(
{"v": v0, "f": f1, "origin": origin, "delta": delta, "faces": faces})
# V1
if v1 not in vert_processed:
vert_processed.append(v1)
faces, origin, delta = slide_uvs(v1, edge, f0, edges, vert_rails, vert_to_uv)
vert_uv_pos.append( {"v":v1, "f":f0, "origin":origin, "delta":delta, "faces":faces} )
faces, origin, delta = slide_uvs(
v1, edge, f0, edges, vert_rails, vert_to_uv)
vert_uv_pos.append(
{"v": v1, "f": f0, "origin": origin, "delta": delta, "faces": faces})
faces, origin, delta = slide_uvs(v1, edge, f1, edges, vert_rails, vert_to_uv)
vert_uv_pos.append( {"v":v1, "f":f1, "origin":origin, "delta":delta, "faces":faces} )
faces, origin, delta = slide_uvs(
v1, edge, f1, edges, vert_rails, vert_to_uv)
vert_uv_pos.append(
{"v": v1, "f": f1, "origin": origin, "delta": delta, "faces": faces})
# ...
for item in vert_uv_pos:
v = item["v"]
for face in item["faces"]:
if v in face.verts:
for loop in face.loops:
if loop.vert == v:
loop[uv_layers].uv= item["origin"] + item["delta"] * (radius/2)
loop[uv_layers].uv = item["origin"] + \
item["delta"] * (radius/2)
# for f in faces:
# for loop in f.loops:
# if loop.vert == vert:
# loop[uv_layers].uv= vert_to_uv[vert][0].uv + item["delta"] * radius/2
# for loop in face.loops:
# if loop.vert == vert:
# loop[uv_layers].uv+= avg_uv_delta
# Restore selection
utilities_uv.selection_restore()
@ -160,7 +152,6 @@ def slide_uvs(vert, edge, face, edges, vert_rails, vert_to_uv):
def IS_DEBUG():
return vert.index == 64 and edge.verts[0].index == 64 and edge.verts[1].index == 63
A = edge.verts[0]
B = edge.verts[1]
A_links, B_links = get_edge_prev_next(edge, edges)
@ -174,7 +165,8 @@ def slide_uvs(vert, edge, face, edges, vert_rails, vert_to_uv):
if IS_DEBUG():
print("\r")
print("Edge {} <--> {} ({})".format(edge.verts[0].index, edge.verts[1].index , vert.index))
print(
"Edge {} <--> {} ({})".format(edge.verts[0].index, edge.verts[1].index, vert.index))
# Collect faces of this side
@ -207,9 +199,9 @@ def slide_uvs(vert, edge, face, edges, vert_rails, vert_to_uv):
if IS_DEBUG():
print(" Faces {}x = {}".format(len(faces), [f.index for f in faces]))
# Get all face edges that could be valid rails
face_edges = list(set([e for f in faces for e in f.edges if e not in edges]))
face_edges = list(
set([e for f in faces for e in f.edges if e not in edges]))
# The verts influencing the offset
verts = [A, B]
@ -223,15 +215,14 @@ def slide_uvs(vert, edge, face, edges, vert_rails, vert_to_uv):
print(" Verts: {}x = {}".format(len(verts), [v.index for v in verts]))
print(" Rails:")
delta = Vector((0, 0))
count = 0.0
for v in verts:
rails = [e for e in vert_rails[v] if e in face_edges]
if IS_DEBUG():
print(" #{} rails = {}".format(v.index, [("{} - {}".format(e.verts[0].index, e.verts[1].index)) for e in rails]))
print(" #{} rails = {}".format(v.index, [
("{} - {}".format(e.verts[0].index, e.verts[1].index)) for e in rails]))
for e in rails:
# determine order
@ -261,7 +252,6 @@ def slide_uvs(vert, edge, face, edges, vert_rails, vert_to_uv):
# loop[uv_layers].uv+= avg_uv_delta
'''
def slide_face_uvs(uv_layers, edge, vert, face, radius, vert_to_uv):
avg_target = Vector((0,0))
@ -287,12 +277,6 @@ def slide_face_uvs(uv_layers, edge, vert, face, radius, vert_to_uv):
'''
'''
# Get all rails (max 3x: current, before and after)
rails = [e for v in verts_edges for e in vert_rails[v]]
@ -310,8 +294,6 @@ def slide_face_uvs(uv_layers, edge, vert, face, radius, vert_to_uv):
'''
def get_edge_prev_next(edge, edges):
A = edge.verts[0]
B = edge.verts[1]
@ -322,8 +304,10 @@ def get_edge_prev_next(edge, edges):
# v1_extends = [v for e in edges for v in e.verts if v in edge.verts and e != edge and v != v1]
# v0_extends = [v_nest for v in edge.verts for e in v.link_edges for v_nest in e.verts if e != edge and if e in edges]
A_extends = [v2 for v1 in edge.verts for e in v1.link_edges for v2 in e.verts if e != edge and e in edges and v2 not in edge.verts and v1 != A]
B_extends = [v2 for v1 in edge.verts for e in v1.link_edges for v2 in e.verts if e != edge and e in edges and v2 not in edge.verts and v1 != B]
A_extends = [v2 for v1 in edge.verts for e in v1.link_edges for v2 in e.verts if e !=
edge and e in edges and v2 not in edge.verts and v1 != A]
B_extends = [v2 for v1 in edge.verts for e in v1.link_edges for v2 in e.verts if e !=
edge and e in edges and v2 not in edge.verts and v1 != B]
return A_extends, B_extends
@ -342,7 +326,6 @@ def get_edge_face_pairs(edges):
return edge_faces
def get_vert_edge_rails(edges):
vert_rails = {}

@ -8,6 +8,7 @@ from math import pi
from . import utilities_uv
class op(bpy.types.Operator):
bl_idname = "uv.textools_island_align_edge"
bl_label = "Align Island by Edge"
@ -43,7 +44,6 @@ class op(bpy.types.Operator):
return True
def execute(self, context):
# Store selection
utilities_uv.selection_store()
@ -56,14 +56,13 @@ class op(bpy.types.Operator):
return {'FINISHED'}
def main(context):
print("Executing operator_island_align_edge")
bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
uv_layers = bm.loops.layers.uv.verify()
faces_selected = [];
faces_selected = []
for face in bm.faces:
if face.select:
for loop in face.loops:
@ -91,11 +90,11 @@ def main(context):
if face in faces_unparsed:
bpy.ops.uv.select_all(action='DESELECT')
face_uvs[face][0].select = True;
face_uvs[face][0].select = True
bpy.ops.uv.select_linked() # Extend selection
# Collect faces
faces_island = [face];
faces_island = [face]
for f in faces_unparsed:
if f != face and f.select and f.loops[0][uv_layers].select:
print("append "+str(f.index))
@ -110,7 +109,8 @@ def main(context):
# Align each island to its edges
for face in faces_islands:
align_island(face_uvs[face][0].uv, face_uvs[face][1].uv, faces_islands[face])
align_island(face_uvs[face][0].uv, face_uvs[face]
[1].uv, faces_islands[face])
def align_island(uv_vert0, uv_vert1, faces):
@ -136,7 +136,8 @@ def align_island(uv_vert0, uv_vert1, faces):
if angle >= (math.pi/4):
angle = angle - (math.pi/2)
bpy.ops.transform.rotate(value=angle, orient_axis='Z', constraint_axis=(False, False, False), orient_type='GLOBAL', mirror=False, use_proportional_edit=False)
bpy.ops.transform.rotate(value=angle, orient_axis='Z', constraint_axis=(
False, False, False), orient_type='GLOBAL', mirror=False, use_proportional_edit=False)
bpy.utils.register_class(op)

@ -18,9 +18,10 @@ class op(bpy.types.Operator):
bl_description = "Rotates UV islands to minimal bounds and sorts them horizontal or vertical"
bl_options = {'REGISTER', 'UNDO'}
is_vertical : bpy.props.BoolProperty(description="Vertical or Horizontal orientation", default=True)
padding : bpy.props.FloatProperty(description="Padding between UV islands", default=0.05)
is_vertical: bpy.props.BoolProperty(
description="Vertical or Horizontal orientation", default=True)
padding: bpy.props.FloatProperty(
description="Padding between UV islands", default=0.05)
@classmethod
def poll(cls, context):
@ -41,7 +42,8 @@ class op(bpy.types.Operator):
# Requires UV map
if not bpy.context.object.data.uv_layers:
return False #self.report({'WARNING'}, "Object must have more than one UV map")
# self.report({'WARNING'}, "Object must have more than one UV map")
return False
# Not in Synced mode
if bpy.context.scene.tool_settings.use_uv_select_sync:
@ -49,7 +51,6 @@ class op(bpy.types.Operator):
return True
def execute(self, context):
main(context, self.is_vertical, self.padding)
return {'FINISHED'}
@ -68,13 +69,11 @@ def main(context, isVertical, padding):
if bpy.context.scene.tool_settings.uv_select_mode is not 'FACE' or 'ISLAND':
bpy.context.scene.tool_settings.uv_select_mode = 'FACE'
bm = bmesh.from_edit_mesh(bpy.context.active_object.data);
uv_layers = bm.loops.layers.uv.verify();
bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
uv_layers = bm.loops.layers.uv.verify()
boundsAll = utilities_uv.getSelectionBBox()
islands = utilities_uv.getSelectionIslands()
allSizes = {} # https://stackoverflow.com/questions/613183/sort-a-python-dictionary-by-value
allBounds = {}
@ -89,17 +88,18 @@ def main(context, isVertical, padding):
# Collect BBox sizes
bounds = utilities_uv.getSelectionBBox()
allSizes[i] = max(bounds['width'], bounds['height']) + i*0.000001;#Make each size unique
allBounds[i] = bounds;
allSizes[i] = max(bounds['width'], bounds['height']) + \
i*0.000001 # Make each size unique
allBounds[i] = bounds
print("Rotate compact: "+str(allSizes[i]))
bpy.context.window_manager.progress_update(i)
bpy.context.window_manager.progress_end()
# Position by sorted size in row
sortedSizes = sorted(allSizes.items(), key=operator.itemgetter(1))#Sort by values, store tuples
# Sort by values, store tuples
sortedSizes = sorted(allSizes.items(), key=operator.itemgetter(1))
sortedSizes.reverse()
offset = 0.0
for sortedSize in sortedSizes:
@ -113,16 +113,17 @@ def main(context, isVertical, padding):
# Offset Island
if(isVertical):
delta = Vector((boundsAll['min'].x - bounds['min'].x, boundsAll['max'].y - bounds['max'].y));
delta = Vector(
(boundsAll['min'].x - bounds['min'].x, boundsAll['max'].y - bounds['max'].y))
bpy.ops.transform.translate(value=(delta.x, delta.y-offset, 0))
offset += bounds['height']+padding
else:
print("Horizontal")
delta = Vector((boundsAll['min'].x - bounds['min'].x, boundsAll['max'].y - bounds['max'].y));
delta = Vector(
(boundsAll['min'].x - bounds['min'].x, boundsAll['max'].y - bounds['max'].y))
bpy.ops.transform.translate(value=(delta.x+offset, delta.y, 0))
offset += bounds['width']+padding
# Restore selection
utilities_uv.selection_restore()
@ -133,13 +134,14 @@ def alignIslandMinimalBounds(uv_layers, faces):
utilities_uv.set_selected_faces(faces)
steps = 8
angle = 45; # Starting Angle, half each step
angle = 45 # Starting Angle, half each step
bboxPrevious = utilities_uv.getSelectionBBox()
for i in range(0, steps):
# Rotate right
bpy.ops.transform.rotate(value=(angle * math.pi / 180), orient_axis='Z')
bpy.ops.transform.rotate(
value=(angle * math.pi / 180), orient_axis='Z')
bbox = utilities_uv.getSelectionBBox()
if i == 0:
@ -147,25 +149,28 @@ def alignIslandMinimalBounds(uv_layers, faces):
sizeB = bbox['width'] * bbox['height']
if abs(bbox['width'] - bbox['height']) <= 0.0001 and sizeA < sizeB:
# print("Already squared")
bpy.ops.transform.rotate(value=(-angle * math.pi / 180), orient_axis='Z')
break;
bpy.ops.transform.rotate(
value=(-angle * math.pi / 180), orient_axis='Z')
break
if bbox['minLength'] < bboxPrevious['minLength']:
bboxPrevious = bbox; # Success
bboxPrevious = bbox # Success
else:
# Rotate Left
bpy.ops.transform.rotate(value=(-angle*2 * math.pi / 180), orient_axis='Z')
bpy.ops.transform.rotate(
value=(-angle*2 * math.pi / 180), orient_axis='Z')
bbox = utilities_uv.getSelectionBBox()
if bbox['minLength'] < bboxPrevious['minLength']:
bboxPrevious = bbox; # Success
bboxPrevious = bbox # Success
else:
# Restore angle of this iteration
bpy.ops.transform.rotate(value=(angle * math.pi / 180), orient_axis='Z')
bpy.ops.transform.rotate(
value=(angle * math.pi / 180), orient_axis='Z')
angle = angle / 2
if bboxPrevious['width'] < bboxPrevious['height']:
bpy.ops.transform.rotate(value=(90 * math.pi / 180), orient_axis='Z')
bpy.utils.register_class(op)

@ -11,8 +11,6 @@ from itertools import chain # 'flattens' collection of iterables
from . import utilities_uv
class op(bpy.types.Operator):
bl_idname = "uv.textools_island_align_world"
bl_label = "Align World"
@ -51,13 +49,11 @@ class op(bpy.types.Operator):
return True
def execute(self, context):
main(self)
return {'FINISHED'}
def main(context):
print("\n________________________\nis_global")
@ -72,13 +68,11 @@ def main(context):
bpy.context.scene.tool_settings.uv_select_mode = 'FACE'
obj = bpy.context.object
bm = bmesh.from_edit_mesh(bpy.context.active_object.data);
uv_layers = bm.loops.layers.uv.verify();
bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
uv_layers = bm.loops.layers.uv.verify()
islands = utilities_uv.getSelectionIslands()
for faces in islands:
# Get average viewport normal of UV island
avg_normal = Vector((0, 0, 0))
@ -98,25 +92,26 @@ def main(context):
for i in range(3):
if(abs(avg_normal.x) == max_size):
print("x normal")
align_island(obj, bm, uv_layers, faces, y, z, avg_normal.x < 0, False)
align_island(obj, bm, uv_layers, faces, y,
z, avg_normal.x < 0, False)
elif(abs(avg_normal.y) == max_size):
print("y normal")
align_island(obj, bm, uv_layers, faces, x, z, avg_normal.y > 0, False)
align_island(obj, bm, uv_layers, faces, x,
z, avg_normal.y > 0, False)
elif(abs(avg_normal.z) == max_size):
print("z normal")
align_island(obj, bm, uv_layers, faces, x, y, False, avg_normal.z < 0)
print("align island: faces {}x n:{}, max:{}".format(len(faces), avg_normal, max_size))
align_island(obj, bm, uv_layers, faces, x,
y, False, avg_normal.z < 0)
print("align island: faces {}x n:{}, max:{}".format(
len(faces), avg_normal, max_size))
# Restore selection
utilities_uv.selection_restore()
def align_island(obj, bm, uv_layers, faces, x=0, y=1, flip_x=False, flip_y=False):
# Find lowest and highest verts
@ -124,14 +119,12 @@ def align_island(obj, bm, uv_layers, faces, x=0, y=1, flip_x=False, flip_y=False
minmax_vert = [None, None]
axis_names = ['x', 'y', 'z']
print("Align shell {}x at {},{} flip {},{}".format(len(faces), axis_names[x], axis_names[y], flip_x, flip_y))
print("Align shell {}x at {},{} flip {},{}".format(
len(faces), axis_names[x], axis_names[y], flip_x, flip_y))
# print(" Min #{} , Max #{} along '{}'".format(minmax_vert[0].index, minmax_vert[1].index, axis_names[y] ))
# print(" A1 {:.1f} , A2 {:.1f} along ".format(minmax_val[0], minmax_val[1] ))
# Collect UV to Vert
vert_to_uv = utilities_uv.get_vert_to_uv(bm, uv_layers)
uv_to_vert = utilities_uv.get_uv_to_vert(bm, uv_layers)
@ -161,7 +154,6 @@ def align_island(obj, bm, uv_layers, faces, x=0, y=1, flip_x=False, flip_y=False
edge.verts[1].co[y] - edge.verts[0].co[y]
))
if flip_x:
delta_verts.x = -edge.verts[1].co[x] + edge.verts[0].co[x]
if flip_y:
@ -169,7 +161,6 @@ def align_island(obj, bm, uv_layers, faces, x=0, y=1, flip_x=False, flip_y=False
# delta_verts.y = edge.verts[0].co[y] - edge.verts[1].co[y]
delta_uvs = Vector((
uv1.uv.x - uv0.uv.x,
uv1.uv.y - uv0.uv.y
@ -177,15 +168,11 @@ def align_island(obj, bm, uv_layers, faces, x=0, y=1, flip_x=False, flip_y=False
a0 = math.atan2(delta_verts.y, delta_verts.x) - math.pi/2
a1 = math.atan2(delta_uvs.y, delta_uvs.x) - math.pi/2
a_delta = math.atan2(math.sin(a0-a1), math.cos(a0-a1))
# edge.verts[0].index, edge.verts[1].index
# print(" turn {:.1f} .. {:.1f} , {:.1f}".format(a_delta*180/math.pi, a0*180/math.pi,a1*180/math.pi))
avg_angle += a_delta
avg_angle /= len(edges) # - math.pi/2
print("Turn {:.1f}".format(avg_angle * 180/math.pi))
@ -194,15 +181,12 @@ def align_island(obj, bm, uv_layers, faces, x=0, y=1, flip_x=False, flip_y=False
for loop in face.loops:
loop[uv_layers].select = True
bpy.context.tool_settings.transform_pivot_point = 'MEDIAN_POINT'
bpy.ops.transform.rotate(value=avg_angle, orient_axis='Z')
# bpy.ops.transform.rotate(value=0.58191, axis=(-0, -0, -1), constraint_axis=(False, False, False), orient_type='GLOBAL', mirror=False, use_proportional_edit=False, proportional_edit_falloff='SPHERE', proportional_size=0.0267348)
# processed = []
'''
bpy.ops.uv.select_all(action='DESELECT')
for face in faces:
@ -288,4 +272,5 @@ def align_island(obj, bm, uv_layers, faces, x=0, y=1, flip_x=False, flip_y=False
# return
'''
bpy.utils.register_class(op)

@ -8,6 +8,7 @@ from math import pi
from . import utilities_uv
class op(bpy.types.Operator):
bl_idname = "uv.textools_island_straighten_edge_loops"
bl_label = "Straight edge loops"
@ -38,18 +39,14 @@ class op(bpy.types.Operator):
if bpy.context.scene.tool_settings.uv_select_mode != 'EDGE':
return False
return True
def execute(self, context):
main(context)
return {'FINISHED'}
def main(context):
print("____________________________")
@ -64,13 +61,10 @@ def main(context):
uvs = utilities_uv.get_selected_uvs(bm, uv_layers)
faces = [f for island in islands for f in island]
# Get island faces
# utilities_uv.selection_restore(bm, uv_layers)
groups = get_edge_groups(bm, uv_layers, faces, edges, uvs)
bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE')
@ -78,16 +72,11 @@ def main(context):
for face in faces:
face.select = True
print("Edges {}x".format(len(edges)))
print("Groups {}x".format(len(groups)))
# Restore 3D face selection
# Restore UV seams and clear pins
bpy.ops.uv.seams_from_islands()
bpy.ops.uv.pin(clear=True)
@ -97,10 +86,6 @@ def main(context):
edge_sets.append(EdgeSet(bm, uv_layers, edges, faces))
# straighten_edges(bm, uv_layers, edges, faces)
sorted_sets = sorted(edge_sets, key=lambda x: x.length, reverse=True)
for edge_set in sorted_sets:
@ -110,7 +95,6 @@ def main(context):
utilities_uv.selection_restore()
class EdgeSet:
bm = None
edges = []
@ -138,9 +122,9 @@ class EdgeSet:
self.edge_length[e] = (uv2 - uv1).length
self.length += self.edge_length[e]
def straighten(self):
print("Straight {}x at {:.2f} length ".format(len(self.edges), self.length))
print("Straight {}x at {:.2f} length ".format(
len(self.edges), self.length))
# Get edge angles in UV space
angles = {}
@ -157,7 +141,8 @@ class EdgeSet:
# Pick edge with least rotation offset to U or V axis
edge_main = sorted(angles.items(), key=operator.itemgetter(1))[0][0]
print("Main edge: {} at {:.2f} degr".format( edge_main.index, angles[edge_main] * 180 / math.pi ))
print("Main edge: {} at {:.2f} degr".format(
edge_main.index, angles[edge_main] * 180 / math.pi))
# Rotate main edge to closest axis
uvs = [uv for v in edge_main.verts for uv in self.vert_to_uv[v]]
@ -171,7 +156,8 @@ class EdgeSet:
if angle >= (math.pi/4):
angle = angle - (math.pi/2)
bpy.ops.uv.cursor_set(location=uv1 + diff/2)
bpy.ops.transform.rotate(value=angle, orient_axis='Z', constraint_axis=(False, False, False), orient_type='GLOBAL', mirror=False, use_proportional_edit=False)
bpy.ops.transform.rotate(value=angle, orient_axis='Z', constraint_axis=(
False, False, False), orient_type='GLOBAL', mirror=False, use_proportional_edit=False)
# Expand edges and straighten
count = len(self.edges)
@ -179,11 +165,13 @@ class EdgeSet:
for i in range(count):
if(len(processed) < len(self.edges)):
verts = set([v for e in processed for v in e.verts])
edges_expand = [e for e in self.edges if e not in processed and (e.verts[0] in verts or e.verts[1] in verts)]
verts_ends = [v for e in edges_expand for v in e.verts if v in verts]
edges_expand = [e for e in self.edges if e not in processed and (
e.verts[0] in verts or e.verts[1] in verts)]
verts_ends = [
v for e in edges_expand for v in e.verts if v in verts]
print("Step, proc {} exp: {}".format( [e.index for e in processed] , [e.index for e in edges_expand] ))
print("Step, proc {} exp: {}".format(
[e.index for e in processed], [e.index for e in edges_expand]))
if len(edges_expand) == 0:
continue
@ -193,24 +181,30 @@ class EdgeSet:
# print("Cancel at edge {}".format(edge.index))
# return
print(" E {} verts {} verts end: {}".format(edge.index, [v.index for v in edge.verts], [v.index for v in verts_ends]))
print(" E {} verts {} verts end: {}".format(edge.index, [
v.index for v in edge.verts], [v.index for v in verts_ends]))
v1 = [v for v in edge.verts if v in verts_ends][0]
v2 = [v for v in edge.verts if v not in verts_ends][0]
# direction
previous_edge = [e for e in processed if e.verts[0] in edge.verts or e.verts[1] in edge.verts][0]
previous_edge = [e for e in processed if e.verts[0]
in edge.verts or e.verts[1] in edge.verts][0]
prev_v1 = [v for v in previous_edge.verts if v != v1][0]
prev_v2 = [v for v in previous_edge.verts if v == v1][0]
direction = (self.vert_to_uv[prev_v2][0].uv - self.vert_to_uv[prev_v1][0].uv).normalized()
direction = (
self.vert_to_uv[prev_v2][0].uv - self.vert_to_uv[prev_v1][0].uv).normalized()
for uv in self.vert_to_uv[v2]:
uv.uv = self.vert_to_uv[v1][0].uv + direction * self.edge_length[edge]
uv.uv = self.vert_to_uv[v1][0].uv + \
direction * self.edge_length[edge]
print("Procesed {}x Expand {}x".format(len(processed), len(edges_expand) ))
print("Procesed {}x Expand {}x".format(
len(processed), len(edges_expand)))
print("verts_ends: {}x".format(len(verts_ends)))
processed.extend(edges_expand)
# Select edges
uvs = list(set( [uv for e in self.edges for v in e.verts for uv in self.vert_to_uv[v] ] ))
uvs = list(
set([uv for e in self.edges for v in e.verts for uv in self.vert_to_uv[v]]))
bpy.ops.uv.select_all(action='DESELECT')
for uv in uvs:
uv.select = True
@ -221,12 +215,6 @@ class EdgeSet:
bpy.ops.uv.pin(clear=True)
def get_edge_groups(bm, uv_layers, faces, edges, uvs):
print("Get edge groups, edges {}x".format(len(edges))+"x")
@ -253,7 +241,8 @@ def get_edge_groups(bm, uv_layers, faces, edges, uvs):
if e in unmatched:
unmatched.remove(e)
print(" Edge {} : Group: {}x , unmatched: {}".format(edge.index, len(group), len(unmatched)))
print(" Edge {} : Group: {}x , unmatched: {}".format(
edge.index, len(group), len(unmatched)))
# return
# group = [edge]
@ -262,11 +251,7 @@ def get_edge_groups(bm, uv_layers, faces, edges, uvs):
# unmatched.remove(e)
# group.append(edge)
return groups
bpy.utils.register_class(op)

@ -9,8 +9,6 @@ import math
from . import utilities_meshtex
class op(bpy.types.Operator):
bl_idname = "uv.textools_meshtex_trim"
bl_label = "Trim"
@ -36,7 +34,6 @@ class op(bpy.types.Operator):
return {'FINISHED'}
def trim(self):
# Wrap the mesh texture around the
print("Trim Mesh Texture :)")
@ -48,17 +45,17 @@ def trim(self):
return
# Collect texture meshes
obj_textures = utilities_meshtex.find_texture_meshes( bpy.context.selected_objects )
obj_textures = utilities_meshtex.find_texture_meshes(
bpy.context.selected_objects)
if len(obj_textures) == 0:
self.report({'ERROR_INVALID_INPUT'}, "No meshes found for mesh textures" )
self.report({'ERROR_INVALID_INPUT'},
"No meshes found for mesh textures")
return
# Setup Thickness
utilities_meshtex.uv_mesh_fit(obj_uv, obj_textures)
# Apply bool modifier to trim
for obj in obj_textures:
name = "Trim UV"
@ -69,9 +66,8 @@ def trim(self):
modifier_bool = obj.modifiers.new(name=name, type='BOOLEAN')
modifier_bool.object = obj_uv
bpy.ops.ui.textools_popup('INVOKE_DEFAULT', message="Collapse modifiers before wrapping")
bpy.ops.ui.textools_popup(
'INVOKE_DEFAULT', message="Collapse modifiers before wrapping")
bpy.utils.register_class(op)

@ -33,20 +33,17 @@ class op(bpy.types.Operator):
return True
def execute(self, context):
rectify(self, context)
return {'FINISHED'}
precision = 3
def rectify(self, context):
obj = bpy.context.active_object
bm = bmesh.from_edit_mesh(obj.data)
uv_layers = bm.loops.layers.uv.verify()
@ -75,9 +72,11 @@ def main(square = False, snapToClosest = False):
# operator.report({'ERROR'}, "selected more than " +str(allowedFaces) +" allowed faces.")
# return
edgeVerts, filteredVerts, selFaces, nonQuadFaces, vertsDict, noEdge = ListsOfVerts(uv_layers, bm)
edgeVerts, filteredVerts, selFaces, nonQuadFaces, vertsDict, noEdge = ListsOfVerts(
uv_layers, bm)
if len(filteredVerts) is 0: return
if len(filteredVerts) is 0:
return
if len(filteredVerts) is 1:
SnapCursorToClosestSelected(filteredVerts)
return
@ -96,10 +95,10 @@ def main(square = False, snapToClosest = False):
ScaleTo0OnAxisAndCursor(filteredVerts, vertsDict, cursorClosestTo)
return SuccessFinished(me, startTime)
MakeEqualDistanceBetweenVertsInLine(filteredVerts, vertsDict, cursorClosestTo)
MakeEqualDistanceBetweenVertsInLine(
filteredVerts, vertsDict, cursorClosestTo)
return SuccessFinished(me, startTime)
# else:
# active face checks
@ -118,8 +117,10 @@ def main(square = False, snapToClosest = False):
luv = l[uv_layers]
luv.select = False
if square: FollowActiveUV(operator, me, targetFace, selFaces, 'EVEN')
else: FollowActiveUV(operator, me, targetFace, selFaces)
if square:
FollowActiveUV(operator, me, targetFace, selFaces, 'EVEN')
else:
FollowActiveUV(operator, me, targetFace, selFaces)
if noEdge is False:
# edge has ripped so we connect it back
@ -132,7 +133,6 @@ def main(square = False, snapToClosest = False):
return SuccessFinished(me, startTime)
def ListsOfVerts(uv_layers, bm):
edgeVerts = []
allEdgeVerts = []
@ -152,7 +152,8 @@ def ListsOfVerts(uv_layers, bm):
luv = l[uv_layers]
if luv.select is True:
facesEdgeVerts.append(luv)
else: isFaceSel = False
else:
isFaceSel = False
allEdgeVerts.extend(facesEdgeVerts)
if isFaceSel:
@ -168,7 +169,8 @@ def ListsOfVerts(uv_layers, bm):
y = round(luv.uv.y, precision)
vertsDict[(x, y)].append(luv)
else: edgeVerts.extend(facesEdgeVerts)
else:
edgeVerts.extend(facesEdgeVerts)
noEdge = False
if len(edgeVerts) is 0:
@ -179,12 +181,12 @@ def ListsOfVerts(uv_layers, bm):
for ev in edgeVerts:
if ListQuasiContainsVect(filteredVerts, ev) is False:
filteredVerts.append(ev)
else: filteredVerts = edgeVerts
else:
filteredVerts = edgeVerts
return edgeVerts, filteredVerts, selFaces, nonQuadFaces, vertsDict, noEdge
def ListQuasiContainsVect(list, vect):
for v in list:
if AreVertsQuasiEqual(v, vect):
@ -192,7 +194,6 @@ def ListQuasiContainsVect(list, vect):
return False
def SnapCursorToClosestSelected(filteredVerts):
# TODO: snap to closest selected
if len(filteredVerts) is 1:
@ -201,9 +202,6 @@ def SnapCursorToClosestSelected(filteredVerts):
return
def VertsDictForLine(uv_layers, bm, selVerts, vertsDict):
for f in bm.faces:
for l in f.loops:
@ -215,7 +213,6 @@ def VertsDictForLine(uv_layers, bm, selVerts, vertsDict):
vertsDict[(x, y)].append(luv)
def AreVectsLinedOnAxis(verts):
areLinedX = True
areLinedY = True
@ -230,7 +227,6 @@ def AreVectsLinedOnAxis(verts):
return areLinedX or areLinedY
def ScaleTo0OnAxisAndCursor(filteredVerts, vertsDict, startv=None, horizontal=None):
verts = filteredVerts
@ -271,7 +267,6 @@ def ScaleTo0OnAxisAndCursor(filteredVerts, vertsDict, startv = None, horizontal
return
def SetAll2dCursorsTo(x, y):
last_area = bpy.context.area.type
bpy.context.area.type = 'IMAGE_EDITOR'
@ -282,7 +277,6 @@ def SetAll2dCursorsTo(x,y):
return
def CursorClosestTo(verts, allowedError=0.025):
ratioX, ratioY = ImageRatio()
@ -290,7 +284,8 @@ def CursorClosestTo(verts, allowedError = 0.025):
min = 1000
minV = verts[0]
for v in verts:
if v is None: continue
if v is None:
continue
for area in bpy.context.screen.areas:
if area.type == 'IMAGE_EDITOR':
loc = area.spaces[0].cursor_location
@ -304,8 +299,6 @@ def CursorClosestTo(verts, allowedError = 0.025):
return None
def SuccessFinished(me, startTime):
# use for backtrack of steps
# bpy.ops.ed.undo_push()
@ -315,7 +308,6 @@ def SuccessFinished(me, startTime):
return
def ShapeFace(uv_layers, operator, targetFace, vertsDict, square):
corners = []
for l in targetFace.loops:
@ -336,16 +328,20 @@ def ShapeFace(uv_layers, operator, targetFace, vertsDict, square):
return
def MakeUvFaceEqualRectangle(vertsDict, lucv, rucv, rdcv, ldcv, startv, square=False):
ratioX, ratioY = ImageRatio()
ratio = ratioX/ratioY
if startv is None: startv = lucv.uv
elif AreVertsQuasiEqual(startv, rucv): startv = rucv.uv
elif AreVertsQuasiEqual(startv, rdcv): startv = rdcv.uv
elif AreVertsQuasiEqual(startv, ldcv): startv = ldcv.uv
else: startv = lucv.uv
if startv is None:
startv = lucv.uv
elif AreVertsQuasiEqual(startv, rucv):
startv = rucv.uv
elif AreVertsQuasiEqual(startv, rdcv):
startv = rdcv.uv
elif AreVertsQuasiEqual(startv, ldcv):
startv = ldcv.uv
else:
startv = lucv.uv
lucv = lucv.uv
rucv = rucv.uv
@ -376,7 +372,8 @@ def MakeUvFaceEqualRectangle(vertsDict, lucv, rucv, rdcv, ldcv, startv, square =
currRowX = ldcv.x
currRowY = ldcv.y + finalScaleY
if square: finalScaleY = finalScaleX*ratio
if square:
finalScaleY = finalScaleX*ratio
#lucv, rucv
x = round(lucv.x, precision)
y = round(lucv.y, precision)
@ -403,11 +400,9 @@ def MakeUvFaceEqualRectangle(vertsDict, lucv, rucv, rdcv, ldcv, startv, square =
v.uv.x = currRowX
v.uv.y = currRowY - finalScaleY
return
def FollowActiveUV(operator, me, f_act, faces, EXTEND_MODE='LENGTH_AVERAGE'):
bm = bmesh.from_edit_mesh(me)
uv_act = bm.loops.layers.uv.active
@ -514,7 +509,8 @@ def FollowActiveUV(operator, me, f_act, faces, EXTEND_MODE = 'LENGTH_AVERAGE'):
l_b_uv = [l[uv_act].uv for l in l_b]
if EXTEND_MODE == 'LENGTH_AVERAGE':
fac = edge_lengths[l_b[2].edge.index][0] / edge_lengths[l_a[1].edge.index][0]
fac = edge_lengths[l_b[2].edge.index][0] / \
edge_lengths[l_a[1].edge.index][0]
elif EXTEND_MODE == 'LENGTH':
a0, b0, c0 = l_a[3].vert.co, l_a[0].vert.co, l_b[3].vert.co
a1, b1, c1 = l_a[2].vert.co, l_a[1].vert.co, l_b[2].vert.co
@ -541,7 +537,8 @@ def FollowActiveUV(operator, me, f_act, faces, EXTEND_MODE = 'LENGTH_AVERAGE'):
if EXTEND_MODE == 'LENGTH_AVERAGE':
bm.edges.index_update()
edge_lengths = [None] * len(bm.edges) #NoneType times the length of edges list
# NoneType times the length of edges list
edge_lengths = [None] * len(bm.edges)
for f in faces:
# we know its a quad
@ -564,7 +561,8 @@ def FollowActiveUV(operator, me, f_act, faces, EXTEND_MODE = 'LENGTH_AVERAGE'):
edge_length_accum += e.calc_length()
edge_length_total += 1
edge_length_store[0] = edge_length_accum / edge_length_total
edge_length_store[0] = edge_length_accum / \
edge_length_total
# done with average length
# ------------------------
@ -587,7 +585,6 @@ def ImageRatio():
return ratioX, ratioY
def Corners(corners):
firstHighest = corners[0]
for c in corners:
@ -621,17 +618,15 @@ def Corners(corners):
return leftUp, leftDown, rightUp, rightDown
def AreVertsQuasiEqual(v1, v2, allowedError=0.0009):
if abs(v1.uv.x - v2.uv.x) < allowedError and abs(v1.uv.y - v2.uv.y) < allowedError:
return True
return False
def hypotVert(v1, v2):
hyp = hypot(v1.x - v2.x, v1.y - v2.y)
return hyp
bpy.utils.register_class(op)

@ -9,6 +9,7 @@ from collections import defaultdict
from . import utilities_texel
from . import utilities_uv
class op(bpy.types.Operator):
bl_idname = "uv.textools_texel_density_set"
bl_label = "Set Texel size"
@ -42,7 +43,6 @@ class op(bpy.types.Operator):
return True
def execute(self, context):
set_texel_density(
self,
@ -53,7 +53,6 @@ class op(bpy.types.Operator):
return {'FINISHED'}
def set_texel_density(self, context, mode, density):
print("Set texel density!")
@ -61,7 +60,6 @@ def set_texel_density(self, context, mode, density):
is_sync = bpy.context.scene.tool_settings.use_uv_select_sync
object_faces = utilities_texel.get_selected_object_faces()
# Warning: No valid input objects
if len(object_faces) == 0:
self.report({'ERROR_INVALID_INPUT'}, "No valid meshes or UV maps")
@ -76,10 +74,10 @@ def set_texel_density(self, context, mode, density):
# Warning: No valid images
if len(object_images) == 0:
self.report({'ERROR_INVALID_INPUT'}, "No Texture found. Assign Checker map or texture." )
self.report({'ERROR_INVALID_INPUT'},
"No Texture found. Assign Checker map or texture.")
return
for obj in object_faces:
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
@ -118,7 +116,6 @@ def set_texel_density(self, context, mode, density):
print("group_faces {}x".format(len(group_faces)))
for group in group_faces:
# Get triangle areas
sum_area_vt = 0
@ -126,7 +123,8 @@ def set_texel_density(self, context, mode, density):
for face in group:
# Triangle Verts
triangle_uv = [loop[uv_layers].uv for loop in face.loops]
triangle_vt = [obj.matrix_world @ vert.co for vert in face.verts]
triangle_vt = [obj.matrix_world @
vert.co for vert in face.verts]
# Triangle Areas
face_area_vt = utilities_texel.get_area_triangle(
@ -143,10 +141,12 @@ def set_texel_density(self, context, mode, density):
)
sum_area_vt += math.sqrt(face_area_vt)
sum_area_uv+= math.sqrt( face_area_uv ) * min(image.size[0], image.size[1])
sum_area_uv += math.sqrt(face_area_uv) * \
min(image.size[0], image.size[1])
# Apply scale to group
print("scale: {:.2f} {:.2f} {:.2f} ".format(density, sum_area_uv, sum_area_vt))
print("scale: {:.2f} {:.2f} {:.2f} ".format(
density, sum_area_uv, sum_area_vt))
scale = 0
if density > 0 and sum_area_uv > 0 and sum_area_vt > 0:
scale = density / (sum_area_uv / sum_area_vt)
@ -166,7 +166,8 @@ def set_texel_density(self, context, mode, density):
loop[uv_layers].select = True
print("Scale: {} {}x".format(scale, len(group)))
bpy.ops.transform.resize(value=(scale, scale, 1), use_proportional_edit=False)
bpy.ops.transform.resize(
value=(scale, scale, 1), use_proportional_edit=False)
# Restore selection
utilities_uv.selection_restore()
@ -186,4 +187,5 @@ def set_texel_density(self, context, mode, density):
if is_sync:
bpy.context.scene.tool_settings.use_uv_select_sync = True
bpy.utils.register_class(op)

@ -2,7 +2,9 @@ import bpy
import bmesh
import operator
import math
import os, sys, subprocess
import os
import sys
import subprocess
from . import settings
from . import utilities_bake
@ -27,7 +29,6 @@ class op(bpy.types.Operator):
return {'FINISHED'}
def open_texture(self, context):
print("Info")

@ -12,13 +12,13 @@ from . import utilities_bake
material_prefix = "TT_atlas_"
gamma = 2.2
class op(bpy.types.Operator):
bl_idname = "uv.textools_texture_preview"
bl_label = "Preview Texture"
bl_description = "Preview the current UV image view background image on the selected object."
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
if not bpy.context.active_object:
@ -40,11 +40,11 @@ class op(bpy.types.Operator):
return {'FINISHED'}
def preview_texture(self, context):
# Collect all low objects from bake sets
objects = [obj for s in settings.sets for obj in s.objects_low if obj.data.uv_layers]
objects = [
obj for s in settings.sets for obj in s.objects_low if obj.data.uv_layers]
# Get view 3D area
view_area = None
@ -57,7 +57,6 @@ def preview_texture(self, context):
# bpy.ops.view3d.localview({'area': view_area})
# return
# Get background image
image = None
for area in bpy.context.screen.areas:
@ -79,10 +78,10 @@ def preview_texture(self, context):
# Create material with image
bpy.ops.object.material_slot_add()
obj.material_slots[0].material = utilities_bake.get_image_material(image)
obj.material_slots[0].material = utilities_bake.get_image_material(
image)
obj.display_type = 'TEXTURED'
# Re-Select objects
bpy.ops.object.select_all(action='DESELECT')
for obj in objects:
@ -98,4 +97,5 @@ def preview_texture(self, context):
# bpy.ops.view3d.localview({'area': view_area})
# bpy.ops.ui.textools_popup('INVOKE_DEFAULT', message="Object is in isolated view")
bpy.utils.register_class(op)

@ -8,6 +8,7 @@ from math import pi
from . import utilities_uv
from . import utilities_ui
class op(bpy.types.Operator):
bl_idname = "uv.textools_unwrap_edge_peel"
bl_label = "Peel Edge"
@ -43,10 +44,10 @@ def unwrap_edges_pipe(self, context):
bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
uv_layers = bm.loops.layers.uv.verify()
contextViewUV = utilities_ui.GetContextViewUV()
if not contextViewUV:
self.report({'ERROR_INVALID_INPUT'}, "This tool requires an available UV/Image view.")
self.report({'ERROR_INVALID_INPUT'},
"This tool requires an available UV/Image view.")
return
# selected_initial = [edge for edge in bm.edges if edge.select]
@ -92,4 +93,5 @@ def unwrap_edges_pipe(self, context):
# TODO: Restore initial selection
bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE')
bpy.utils.register_class(op)

@ -8,6 +8,7 @@ from math import pi
from . import utilities_uv
from . import utilities_ui
class op(bpy.types.Operator):
bl_idname = "uv.textools_uv_crop"
bl_label = "Crop"
@ -41,26 +42,27 @@ class op(bpy.types.Operator):
return {'FINISHED'}
def crop(self, context):
bm = bmesh.from_edit_mesh(bpy.context.active_object.data);
uv_layers = bm.loops.layers.uv.verify();
bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
uv_layers = bm.loops.layers.uv.verify()
padding = utilities_ui.get_padding()
# Scale to fit bounds
bbox = utilities_uv.getSelectionBBox()
scale_u = (1.0-padding) / bbox['width']
scale_v = (1.0-padding) / bbox['height']
scale = min(scale_u, scale_v)
bpy.ops.transform.resize(value=(scale, scale, scale), constraint_axis=(False, False, False), mirror=False, use_proportional_edit=False)
bpy.ops.transform.resize(value=(scale, scale, scale), constraint_axis=(
False, False, False), mirror=False, use_proportional_edit=False)
# Reposition
bbox = utilities_uv.getSelectionBBox()
delta_position = Vector((padding/2,1-padding/2)) - Vector((bbox['min'].x, bbox['min'].y + bbox['height']))
delta_position = Vector((padding/2, 1-padding/2)) - \
Vector((bbox['min'].x, bbox['min'].y + bbox['height']))
bpy.ops.transform.translate(value=(delta_position.x, delta_position.y, 0))
bpy.utils.register_class(op)

@ -18,11 +18,11 @@ utilities_ui.icon_register("op_extend_canvas_BL_active.png")
utilities_ui.icon_register("op_extend_canvas_BR_active.png")
def on_dropdown_size_x(self, context):
self.size_x = int(self.dropdown_size_x)
# context.area.tag_redraw()
def on_dropdown_size_y(self, context):
self.size_y = int(self.dropdown_size_y)
# context.area.tag_redraw()
@ -62,10 +62,14 @@ class op(bpy.types.Operator):
)
direction: bpy.props.EnumProperty(name='direction', items=(
('TL',' ','Top Left', utilities_ui.icon_get("op_extend_canvas_TL_active"),0),
('BL',' ','Bottom Left', utilities_ui.icon_get("op_extend_canvas_BL_active"),2),
('TR',' ','Top Right', utilities_ui.icon_get("op_extend_canvas_TR_active"),1),
('BR',' ','Bottom Right', utilities_ui.icon_get("op_extend_canvas_BR_active"),3)
('TL', ' ', 'Top Left', utilities_ui.icon_get(
"op_extend_canvas_TL_active"), 0),
('BL', ' ', 'Bottom Left', utilities_ui.icon_get(
"op_extend_canvas_BL_active"), 2),
('TR', ' ', 'Top Right', utilities_ui.icon_get(
"op_extend_canvas_TR_active"), 1),
('BR', ' ', 'Bottom Right', utilities_ui.icon_get(
"op_extend_canvas_BR_active"), 3)
))
@classmethod
@ -90,7 +94,6 @@ class op(bpy.types.Operator):
return True
def invoke(self, context, event):
print("Invoke resize area")
self.size_x = bpy.context.scene.texToolsSettings.size[0]
@ -105,7 +108,6 @@ class op(bpy.types.Operator):
self.dropdown_size_y = item[0]
break
return context.window_manager.invoke_props_dialog(self, width=140)
def check(self, context):
@ -115,7 +117,6 @@ class op(bpy.types.Operator):
# https://b3d.interplanety.org/en/creating-pop-up-panels-with-user-ui-in-blender-add-on/
layout = self.layout
layout.separator()
# New Size
@ -136,7 +137,8 @@ class op(bpy.types.Operator):
row.prop(self, 'direction', expand=True)
# Summary
size_A = "{} x {}".format(bpy.context.scene.texToolsSettings.size[0], bpy.context.scene.texToolsSettings.size[1])
size_A = "{} x {}".format(
bpy.context.scene.texToolsSettings.size[0], bpy.context.scene.texToolsSettings.size[1])
if bpy.context.scene.texToolsSettings.size[0] == bpy.context.scene.texToolsSettings.size[1]:
size_A = "{}²".format(bpy.context.scene.texToolsSettings.size[0])
size_B = "{} x {}".format(self.size_x, self.size_y)
@ -147,10 +149,8 @@ class op(bpy.types.Operator):
size_A, size_B
))
layout.separator()
def execute(self, context):
# Store selection
@ -189,7 +189,6 @@ class op(bpy.types.Operator):
return {'FINISHED'}
def resize_uv(self, context, mode, size_A, size_B):
# Set pivot
@ -209,8 +208,8 @@ def resize_uv(self, context, mode, size_A, size_B):
# Resize
scale_x = size_A.x / size_B.x
scale_y = size_A.y / size_B.y
bpy.ops.transform.resize(value=(scale_x, scale_y, 1.0), use_proportional_edit=False)
bpy.ops.transform.resize(
value=(scale_x, scale_y, 1.0), use_proportional_edit=False)
def resize_image(context, mode, size_A, size_B):
@ -219,14 +218,15 @@ def resize_image(context, mode, size_A, size_B):
# Notes: https://blender.stackexchange.com/questions/31514/active-image-of-uv-image-editor
# https://docs.blender.org/api/blender_python_api_2_70_4/bpy.types.SpaceImageEditor.html
if context.area.spaces.active != None:
if context.area.spaces.active.image != None:
image = context.area.spaces.active.image
image_obj = utilities_texel.get_object_texture_image(bpy.context.active_object)
image_obj = utilities_texel.get_object_texture_image(
bpy.context.active_object)
if name_texture in image.name or image == image_obj:
# Resize Image UV editor background image
utilities_texel.image_resize(image, int(size_B.x), int(size_B.y))
utilities_texel.image_resize(
image, int(size_B.x), int(size_B.y))
else:
# No Image assigned
@ -247,7 +247,8 @@ def resize_image(context, mode, size_A, size_B):
image.generated_height = int(size_B.y)
else:
# Create new image
image = bpy.data.images.new(name_texture, width=int(size_B.x), height=int(size_B.y))
image = bpy.data.images.new(
name_texture, width=int(size_B.x), height=int(size_B.y))
image.generated_color = (color.r, color.g, color.b, 1.0)
image.generated_type = 'BLANK'
image.generated_width = int(size_B.x)
@ -260,5 +261,4 @@ def resize_image(context, mode, size_A, size_B):
utilities_texel.checker_images_cleanup()
bpy.utils.register_class(op)

@ -20,7 +20,6 @@ keywords_float = ['floater','float','f']
split_chars = [' ', '_', '.', '-']
class BakeMode:
material = "" # Material name from external blend file
type = 'EMIT'
@ -44,7 +43,6 @@ class BakeMode:
self.use_project = use_project
def on_select_bake_mode(mode):
print("Mode changed {}".format(mode))
@ -81,8 +79,6 @@ def store_bake_settings():
settings.bake_objects_hide_render = []
# for obj in bpy.context.view_layer.objects:
# if obj.hide_render == False and obj not in objects_sets:
# Check if layer is active:
@ -96,7 +92,6 @@ def store_bake_settings():
# obj.cycles_visibility.shadow = False
def restore_bake_settings():
# Render Settings
if settings.bake_render_engine != '':
@ -111,15 +106,15 @@ def restore_bake_settings():
# obj.cycles_visibility.shadow = True
stored_materials = {}
stored_material_faces = {}
def store_materials_clear():
stored_materials.clear()
stored_material_faces.clear()
def store_materials(obj):
stored_materials[obj] = []
stored_material_faces[obj] = []
@ -130,14 +125,15 @@ def store_materials(obj):
bpy.context.view_layer.objects.active = obj
bpy.ops.object.mode_set(mode='EDIT')
bm = bmesh.from_edit_mesh(obj.data);
bm = bmesh.from_edit_mesh(obj.data)
# for each slot backup the material
for s in range(len(obj.material_slots)):
slot = obj.material_slots[s]
stored_materials[obj].append(slot.material)
stored_material_faces[obj].append( [face.index for face in bm.faces if face.material_index == s] )
stored_material_faces[obj].append(
[face.index for face in bm.faces if face.material_index == s])
# print("Faces: {}x".format( len(stored_material_faces[obj][-1]) ))
@ -149,13 +145,12 @@ def store_materials(obj):
bpy.ops.object.mode_set(mode='OBJECT')
def restore_materials():
for obj in stored_materials:
# Enter edit mode
bpy.context.view_layer.objects.active = obj
bpy.ops.object.mode_set(mode='EDIT')
bm = bmesh.from_edit_mesh(obj.data);
bm = bmesh.from_edit_mesh(obj.data)
# Restore slots
for index in range(len(stored_materials[obj])):
@ -180,7 +175,6 @@ def restore_materials():
bpy.ops.object.material_slot_remove()
def get_set_name_base(obj):
def remove_digits(name):
@ -202,7 +196,6 @@ def get_set_name_base(obj):
return remove_digits(obj.name).lower()
def get_set_name(obj):
# Get Basic name
name = get_set_name_base(obj)
@ -231,7 +224,6 @@ def get_set_name(obj):
return "_".join(new_strings)
def get_object_type(obj):
name = get_set_name_base(obj)
@ -257,7 +249,6 @@ def get_object_type(obj):
elif modifier.type == 'BEVEL':
return 'high'
# Detect High first, more rare
for string in strings:
for key in keywords_high:
@ -270,20 +261,16 @@ def get_object_type(obj):
if key == string:
return 'cage'
# Detect low
for string in strings:
for key in keywords_low:
if key == string:
return 'low'
# if nothing was detected, assume its low
return 'low'
def get_baked_images(sets):
images = []
for set in sets:
@ -295,7 +282,6 @@ def get_baked_images(sets):
return images
def get_bake_sets():
filtered = {}
for obj in bpy.context.selected_objects:
@ -334,7 +320,6 @@ def get_bake_sets():
groups = sorted_groups
# print("Keys: "+", ".join(keys))
bake_sets = []
for group in groups:
low = []
@ -351,14 +336,12 @@ def get_bake_sets():
elif filtered[obj] == 'float':
float.append(obj)
name = get_set_name(group[0])
bake_sets.append(BakeSet(name, low, cage, high, float))
return bake_sets
class BakeSet:
objects_low = [] # low poly geometry
objects_cage = [] # Cage low poly geometry
@ -390,7 +373,6 @@ class BakeSet:
break
def setup_vertex_color_selection(obj):
bpy.ops.object.mode_set(mode='OBJECT')
@ -398,7 +380,6 @@ def setup_vertex_color_selection(obj):
obj.select_set(state=True, view_layer=None)
bpy.context.view_layer.objects.active = obj
bpy.ops.object.mode_set(mode='VERTEX_PAINT')
bpy.context.tool_settings.vertex_paint.brush.color = (0, 0, 0)
@ -415,7 +396,6 @@ def setup_vertex_color_selection(obj):
bpy.ops.object.mode_set(mode='OBJECT')
def setup_vertex_color_dirty(obj):
print("setup_vertex_color_dirty {}".format(obj.name))
@ -429,7 +409,6 @@ def setup_vertex_color_dirty(obj):
bm = bmesh.from_edit_mesh(obj.data)
colorLayer = bm.loops.layers.color.verify()
color = utilities_color.safe_color((1, 1, 1))
for face in bm.faces:
@ -443,13 +422,11 @@ def setup_vertex_color_dirty(obj):
bpy.ops.paint.vertex_color_dirt()
def setup_vertex_color_id_material(obj):
bpy.ops.object.select_all(action='DESELECT')
obj.select_set(state=True, view_layer=None)
bpy.context.view_layer.objects.active = obj
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE')
@ -482,7 +459,6 @@ def setup_vertex_color_id_material(obj):
bpy.ops.object.mode_set(mode='OBJECT')
def setup_vertex_color_id_element(obj):
bpy.ops.object.select_all(action='DESELECT')
obj.select_set(state=True, view_layer=None)
@ -535,7 +511,6 @@ def get_image_material(image):
else:
material = bpy.data.materials.new(image.name)
# Cyles Material
if bpy.context.scene.render.engine == 'CYCLES' or bpy.context.scene.render.engine == 'BLENDER_EEVEE':
material.use_nodes = True
@ -554,14 +529,14 @@ def get_image_material(image):
# Base Diffuse BSDF
node_diffuse = material.node_tree.nodes['Principled BSDF']
if "_normal_" in image.name:
# Add Normal Map Nodes
node_normal_map = None
if "normal_map" in material.node_tree.nodes:
node_normal_map = material.node_tree.nodes["normal_map"]
else:
node_normal_map = material.node_tree.nodes.new("ShaderNodeNormalMap")
node_normal_map = material.node_tree.nodes.new(
"ShaderNodeNormalMap")
node_normal_map.name = "normal_map"
# Tangent or World space
@ -571,10 +546,12 @@ def get_image_material(image):
node_normal_map.space = 'WORLD'
# image to normal_map link
material.node_tree.links.new(node_image.outputs[0], node_normal_map.inputs[1])
material.node_tree.links.new(
node_image.outputs[0], node_normal_map.inputs[1])
# normal_map to diffuse_bsdf link
material.node_tree.links.new(node_normal_map.outputs[0], node_diffuse.inputs[19])
material.node_tree.links.new(
node_normal_map.outputs[0], node_diffuse.inputs[19])
node_normal_map.location = node_diffuse.location - Vector((200, 0))
node_image.location = node_normal_map.location - Vector((200, 0))
@ -584,7 +561,8 @@ def get_image_material(image):
# dump(node_image.color_mapping.bl_rna.property_tags)
# image node to diffuse node link
material.node_tree.links.new(node_image.outputs[0], node_diffuse.inputs[0])
material.node_tree.links.new(
node_image.outputs[0], node_diffuse.inputs[0])
return material

@ -52,19 +52,17 @@ def assign_color(index):
material.node_tree.nodes["Principled BSDF"].inputs[0].default_value = rgba
material.diffuse_color = rgba
elif not material.use_nodes and bpy.context.scene.render.engine == 'BLENDER_EEVEE':
# Legacy render engine, not suited for baking
material.diffuse_color = rgba
def get_material(index):
name = get_name(index)
# Material already exists?
if name in bpy.data.materials:
material = bpy.data.materials[name];
material = bpy.data.materials[name]
# Check for incorrect matreials for current render engine
if not material:
@ -77,7 +75,7 @@ def get_material(index):
replace_material(index)
else:
return material;
return material
print("Could nt find {} , create a new one??".format(name))
@ -86,7 +84,6 @@ def get_material(index):
return material
# Replaace an existing material with a new one
# This is sometimes necessary after switching the render engine
def replace_material(index):
@ -96,7 +93,7 @@ def replace_material(index):
# Check if material exists
if name in bpy.data.materials:
material = bpy.data.materials[name];
material = bpy.data.materials[name]
# Collect material slots we have to re-assign
slots = []
@ -112,8 +109,7 @@ def replace_material(index):
# Re-assign new material to all previous slots
material = create_material(index)
for slot in slots:
slot.material = material;
slot.material = material
def create_material(index):
@ -130,12 +126,10 @@ def create_material(index):
return material
def get_name(index):
return (material_prefix+"{:02d}").format(index)
def get_color(index):
if index < bpy.context.scene.texToolsSettings.color_ID_count:
return getattr(bpy.context.scene.texToolsSettings, "color_ID_color_{}".format(index))
@ -144,16 +138,15 @@ def get_color(index):
return (0, 0, 0)
def set_color(index, color):
if index < bpy.context.scene.texToolsSettings.color_ID_count:
setattr(bpy.context.scene.texToolsSettings, "color_ID_color_{}".format(index), color)
setattr(bpy.context.scene.texToolsSettings,
"color_ID_color_{}".format(index), color)
def validate_face_colors(obj):
# Validate face colors and material slots
previous_mode = bpy.context.object.mode;
previous_mode = bpy.context.object.mode
count = bpy.context.scene.texToolsSettings.color_ID_count
# Verify enough material slots
@ -165,10 +158,9 @@ def validate_face_colors(obj):
else:
break
# TODO: Check face.material_index
bpy.ops.object.mode_set(mode='EDIT')
bm = bmesh.from_edit_mesh(obj.data);
bm = bmesh.from_edit_mesh(obj.data)
for face in bm.faces:
face.material_index %= count
obj.data.update()
@ -179,17 +171,14 @@ def validate_face_colors(obj):
for i in range(len(obj.material_slots) - count):
if len(obj.material_slots) > count:
# Remove last
bpy.context.object.active_material_index = len(obj.material_slots)-1
bpy.context.object.active_material_index = len(
obj.material_slots)-1
bpy.ops.object.material_slot_remove()
# Restore previous mode
bpy.ops.object.mode_set(mode=previous_mode)
def hex_to_color(hex):
hex = hex.strip('#')
@ -205,7 +194,6 @@ def hex_to_color(hex):
return tuple(fin)
def color_to_hex(color):
rgb = []
for i in range(3):
@ -218,7 +206,6 @@ def color_to_hex(color):
return "#{:02X}{:02X}{:02X}".format(r, g, b)
def get_color_id(index, count):
# Get unique color
color = Color()

@ -25,6 +25,7 @@ size_textures = [
preview_icons = bpy.utils.previews.new()
def icon_get(name):
return preview_icons[name].icon_id
@ -36,7 +37,8 @@ def GetContextView3D():
if area.type == 'VIEW_3D':
for region in area.regions:
if region.type == 'WINDOW':
override = {'window': window, 'screen': screen, 'area': area, 'region': region, 'scene': bpy.context.scene, 'edit_object': bpy.context.edit_object, 'active_object': bpy.context.active_object, 'selected_objects': bpy.context.selected_objects} # Stuff the override context with very common requests by operators. MORE COULD BE NEEDED!
override = {'window': window, 'screen': screen, 'area': area, 'region': region, 'scene': bpy.context.scene, 'edit_object': bpy.context.edit_object,
'active_object': bpy.context.active_object, 'selected_objects': bpy.context.selected_objects} # Stuff the override context with very common requests by operators. MORE COULD BE NEEDED!
return override
return None
@ -48,26 +50,24 @@ def GetContextViewUV():
if area.type == 'IMAGE_EDITOR':
for region in area.regions:
if region.type == 'WINDOW':
override = {'window': window, 'screen': screen, 'area': area, 'region': region, 'scene': bpy.context.scene, 'edit_object': bpy.context.edit_object, 'active_object': bpy.context.active_object, 'selected_objects': bpy.context.selected_objects} # Stuff the override context with very common requests by operators. MORE COULD BE NEEDED!
override = {'window': window, 'screen': screen, 'area': area, 'region': region, 'scene': bpy.context.scene, 'edit_object': bpy.context.edit_object,
'active_object': bpy.context.active_object, 'selected_objects': bpy.context.selected_objects} # Stuff the override context with very common requests by operators. MORE COULD BE NEEDED!
return override
return None
def icon_register(fileName):
name = fileName.split('.')[0] # Don't include file extension
icons_dir = os.path.join(os.path.dirname(__file__), "icons")
preview_icons.load(name, os.path.join(icons_dir, fileName), 'IMAGE')
def get_padding():
size_min = min(bpy.context.scene.texToolsSettings.size[0],bpy.context.scene.texToolsSettings.size[1])
size_min = min(
bpy.context.scene.texToolsSettings.size[0], bpy.context.scene.texToolsSettings.size[1])
return bpy.context.scene.texToolsSettings.padding / size_min
def generate_bake_mode_previews():
# We are accessing all of the information that we generated in the register function below
preview_collection = preview_collections["thumbnail_previews"]
@ -81,7 +81,6 @@ def generate_bake_mode_previews():
mode = image[0:-4]
print(".. .{}".format(mode))
if image.endswith(VALID_EXTENSIONS) and mode in op_bake.modes:
filepath = os.path.join(image_location, image)
thumb = preview_collection.load(filepath, filepath, 'IMAGE')
@ -113,14 +112,11 @@ class op_popup(bpy.types.Operator):
self.layout.label(text=self.message)
def on_bakemode_set(self, context):
print("Set '{}'".format(bpy.context.scene.TT_bake_mode))
utilities_bake.on_select_bake_mode(get_bake_mode())
def register():
from bpy.types import Scene
from bpy.props import StringProperty, EnumProperty
@ -135,10 +131,10 @@ def register():
# Create a new preview collection (only upon register)
preview_collection = bpy.utils.previews.new()
preview_collection.images_location = os.path.join(os.path.dirname(__file__), "resources/bake_modes")
preview_collection.images_location = os.path.join(
os.path.dirname(__file__), "resources/bake_modes")
preview_collections["thumbnail_previews"] = preview_collection
# This is an EnumProperty to hold all of the images
# You really can save it anywhere in bpy.types.* Just make sure the location makes sense
bpy.types.Scene.TT_bake_mode = EnumProperty(
@ -157,14 +153,13 @@ def unregister():
bpy.utils.previews.remove(preview_collection)
preview_collections.clear()
# Unregister icons
# global preview_icons
bpy.utils.previews.remove(preview_icons)
del bpy.types.Scene.TT_bake_mode
if __name__ == "__main__":
register()
bpy.utils.register_class(op_popup)
Loading…
Cancel
Save