mirror of
https://github.com/drewcassidy/TexTools-Blender
synced 2024-09-01 14:54:44 +00:00
PEP8 compliance
This commit is contained in:
parent
f5252922b9
commit
f60b85b477
49
op_align.py
49
op_align.py
@ -13,25 +13,26 @@ class op(bpy.types.Operator):
|
|||||||
bl_label = "Align"
|
bl_label = "Align"
|
||||||
bl_description = "Align vertices, edges or shells"
|
bl_description = "Align vertices, edges or shells"
|
||||||
bl_options = {'REGISTER', 'UNDO'}
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
direction : bpy.props.StringProperty(name="Direction", default="top")
|
direction: bpy.props.StringProperty(name="Direction", default="top")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
if not bpy.context.active_object:
|
if not bpy.context.active_object:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#Only in Edit mode
|
# Only in Edit mode
|
||||||
if bpy.context.active_object.mode != 'EDIT':
|
if bpy.context.active_object.mode != 'EDIT':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#Only in UV editor mode
|
# Only in UV editor mode
|
||||||
if bpy.context.area.type != 'IMAGE_EDITOR':
|
if bpy.context.area.type != 'IMAGE_EDITOR':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#Requires UV map
|
# Requires UV map
|
||||||
if not bpy.context.object.data.uv_layers:
|
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
|
# Not in Synced mode
|
||||||
if bpy.context.scene.tool_settings.use_uv_select_sync:
|
if bpy.context.scene.tool_settings.use_uv_select_sync:
|
||||||
@ -39,27 +40,23 @@ class op(bpy.types.Operator):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
|
|
||||||
align(context, self.direction)
|
align(context, self.direction)
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def align(context, direction):
|
def align(context, direction):
|
||||||
#Store selection
|
# Store selection
|
||||||
utilities_uv.selection_store()
|
utilities_uv.selection_store()
|
||||||
|
|
||||||
if bpy.context.tool_settings.transform_pivot_point != 'CURSOR':
|
if bpy.context.tool_settings.transform_pivot_point != 'CURSOR':
|
||||||
bpy.context.tool_settings.transform_pivot_point = 'CURSOR'
|
bpy.context.tool_settings.transform_pivot_point = 'CURSOR'
|
||||||
|
|
||||||
#B-Mesh
|
# B-Mesh
|
||||||
obj = bpy.context.active_object
|
obj = bpy.context.active_object
|
||||||
bm = bmesh.from_edit_mesh(obj.data);
|
bm = bmesh.from_edit_mesh(obj.data)
|
||||||
uv_layers = bm.loops.layers.uv.verify();
|
uv_layers = bm.loops.layers.uv.verify()
|
||||||
|
|
||||||
if len(obj.data.uv_layers) == 0:
|
if len(obj.data.uv_layers) == 0:
|
||||||
print("There is no UV channel or UV data set")
|
print("There is no UV channel or UV data set")
|
||||||
@ -71,12 +68,12 @@ def align(context, direction):
|
|||||||
mode = bpy.context.scene.tool_settings.uv_select_mode
|
mode = bpy.context.scene.tool_settings.uv_select_mode
|
||||||
if mode == 'FACE' or mode == 'ISLAND':
|
if mode == 'FACE' or mode == 'ISLAND':
|
||||||
print("____ Align Islands")
|
print("____ Align Islands")
|
||||||
|
|
||||||
#Collect UV islands
|
# Collect UV islands
|
||||||
islands = utilities_uv.getSelectionIslands()
|
islands = utilities_uv.getSelectionIslands()
|
||||||
|
|
||||||
for island in islands:
|
for island in islands:
|
||||||
|
|
||||||
bpy.ops.uv.select_all(action='DESELECT')
|
bpy.ops.uv.select_all(action='DESELECT')
|
||||||
utilities_uv.set_selected_faces(island)
|
utilities_uv.set_selected_faces(island)
|
||||||
bounds = utilities_uv.getSelectionBBox()
|
bounds = utilities_uv.getSelectionBBox()
|
||||||
@ -84,13 +81,13 @@ def align(context, direction):
|
|||||||
# print("Island "+str(len(island))+"x faces, delta: "+str(delta.y))
|
# print("Island "+str(len(island))+"x faces, delta: "+str(delta.y))
|
||||||
|
|
||||||
if direction == "bottom":
|
if direction == "bottom":
|
||||||
delta = boundsAll['min'] - bounds['min']
|
delta = boundsAll['min'] - bounds['min']
|
||||||
bpy.ops.transform.translate(value=(0, delta.y, 0))
|
bpy.ops.transform.translate(value=(0, delta.y, 0))
|
||||||
elif direction == "top":
|
elif direction == "top":
|
||||||
delta = boundsAll['max'] - bounds['max']
|
delta = boundsAll['max'] - bounds['max']
|
||||||
bpy.ops.transform.translate(value=(0, delta.y, 0))
|
bpy.ops.transform.translate(value=(0, delta.y, 0))
|
||||||
elif direction == "left":
|
elif direction == "left":
|
||||||
delta = boundsAll['min'] - bounds['min']
|
delta = boundsAll['min'] - bounds['min']
|
||||||
bpy.ops.transform.translate(value=(delta.x, 0, 0))
|
bpy.ops.transform.translate(value=(delta.x, 0, 0))
|
||||||
elif direction == "right":
|
elif direction == "right":
|
||||||
delta = boundsAll['max'] - bounds['max']
|
delta = boundsAll['max'] - bounds['max']
|
||||||
@ -98,7 +95,6 @@ def align(context, direction):
|
|||||||
else:
|
else:
|
||||||
print("Unkown direction: "+str(direction))
|
print("Unkown direction: "+str(direction))
|
||||||
|
|
||||||
|
|
||||||
elif mode == 'EDGE' or mode == 'VERTEX':
|
elif mode == 'EDGE' or mode == 'VERTEX':
|
||||||
print("____ Align Verts")
|
print("____ Align Verts")
|
||||||
|
|
||||||
@ -117,15 +113,10 @@ def align(context, direction):
|
|||||||
elif direction == "right":
|
elif direction == "right":
|
||||||
luv.uv[0] = boundsAll['max'].x
|
luv.uv[0] = boundsAll['max'].x
|
||||||
|
|
||||||
|
|
||||||
bmesh.update_edit_mesh(obj.data)
|
bmesh.update_edit_mesh(obj.data)
|
||||||
|
|
||||||
#Restore selection
|
# Restore selection
|
||||||
utilities_uv.selection_restore()
|
utilities_uv.selection_restore()
|
||||||
|
|
||||||
|
|
||||||
bpy.utils.register_class(op)
|
bpy.utils.register_class(op)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
236
op_bake.py
236
op_bake.py
@ -8,19 +8,19 @@ from random import random
|
|||||||
|
|
||||||
from . import utilities_ui
|
from . import utilities_ui
|
||||||
from . import settings
|
from . import settings
|
||||||
from . import utilities_bake as ub #Use shorthand ub = utitlites_bake
|
from . import utilities_bake as ub # Use shorthand ub = utitlites_bake
|
||||||
|
|
||||||
|
|
||||||
# Notes: https://docs.blender.org/manual/en/dev/render/blender_render/bake.html
|
# Notes: https://docs.blender.org/manual/en/dev/render/blender_render/bake.html
|
||||||
modes={
|
modes = {
|
||||||
'normal_tangent': ub.BakeMode('', type='NORMAL', color=(0.5, 0.5, 1, 1), use_project=True),
|
'normal_tangent': ub.BakeMode('', type='NORMAL', color=(0.5, 0.5, 1, 1), use_project=True),
|
||||||
'normal_object': ub.BakeMode('', type='NORMAL', color=(0.5, 0.5, 1, 1), normal_space='OBJECT' ),
|
'normal_object': ub.BakeMode('', type='NORMAL', color=(0.5, 0.5, 1, 1), normal_space='OBJECT'),
|
||||||
'cavity': ub.BakeMode('bake_cavity', type='EMIT', setVColor=ub.setup_vertex_color_dirty),
|
'cavity': ub.BakeMode('bake_cavity', type='EMIT', setVColor=ub.setup_vertex_color_dirty),
|
||||||
'paint_base': ub.BakeMode('bake_paint_base', type='EMIT'),
|
'paint_base': ub.BakeMode('bake_paint_base', type='EMIT'),
|
||||||
'dust': ub.BakeMode('bake_dust', type='EMIT', setVColor=ub.setup_vertex_color_dirty),
|
'dust': ub.BakeMode('bake_dust', type='EMIT', setVColor=ub.setup_vertex_color_dirty),
|
||||||
'id_element': ub.BakeMode('bake_vertex_color',type='EMIT', setVColor=ub.setup_vertex_color_id_element),
|
'id_element': ub.BakeMode('bake_vertex_color', type='EMIT', setVColor=ub.setup_vertex_color_id_element),
|
||||||
'id_material': ub.BakeMode('bake_vertex_color',type='EMIT', setVColor=ub.setup_vertex_color_id_material),
|
'id_material': ub.BakeMode('bake_vertex_color', type='EMIT', setVColor=ub.setup_vertex_color_id_material),
|
||||||
'selection': ub.BakeMode('bake_vertex_color',type='EMIT', color=(0, 0, 0, 1), setVColor=ub.setup_vertex_color_selection),
|
'selection': ub.BakeMode('bake_vertex_color', type='EMIT', color=(0, 0, 0, 1), setVColor=ub.setup_vertex_color_selection),
|
||||||
'diffuse': ub.BakeMode('', type='DIFFUSE'),
|
'diffuse': ub.BakeMode('', type='DIFFUSE'),
|
||||||
# 'displacment': ub.BakeMode('', type='DISPLACEMENT', use_project=True, color=(0, 0, 0, 1), engine='CYCLES'),
|
# 'displacment': ub.BakeMode('', type='DISPLACEMENT', use_project=True, color=(0, 0, 0, 1), engine='CYCLES'),
|
||||||
'ao': ub.BakeMode('', type='AO', color=(1, 1, 1, 0), params=["bake_samples"], engine='CYCLES'),
|
'ao': ub.BakeMode('', type='AO', color=(1, 1, 1, 0), params=["bake_samples"], engine='CYCLES'),
|
||||||
@ -30,12 +30,14 @@ modes={
|
|||||||
'wireframe': ub.BakeMode('bake_wireframe', type='EMIT', color=(0, 0, 0, 1), params=["bake_wireframe_size"])
|
'wireframe': ub.BakeMode('bake_wireframe', type='EMIT', color=(0, 0, 0, 1), params=["bake_wireframe_size"])
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasattr(bpy.types,"ShaderNodeBevel"):
|
if hasattr(bpy.types, "ShaderNodeBevel"):
|
||||||
# Has newer bevel shader (2.7 nightly build series)
|
# 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['bevel_mask'] = ub.BakeMode('bake_bevel_mask', type='EMIT', color=(
|
||||||
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"])
|
0, 0, 0, 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['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):
|
class op(bpy.types.Operator):
|
||||||
@ -53,43 +55,45 @@ class op(bpy.types.Operator):
|
|||||||
bake_mode = utilities_ui.get_bake_mode()
|
bake_mode = utilities_ui.get_bake_mode()
|
||||||
|
|
||||||
if bake_mode not in modes:
|
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
|
return
|
||||||
|
|
||||||
# Store Selection
|
# Store Selection
|
||||||
selected_objects = [obj for obj in bpy.context.selected_objects]
|
selected_objects = [obj for obj in bpy.context.selected_objects]
|
||||||
active_object = bpy.context.view_layer.objects.active
|
active_object = bpy.context.view_layer.objects.active
|
||||||
ub.store_bake_settings()
|
ub.store_bake_settings()
|
||||||
|
|
||||||
# Render sets
|
# Render sets
|
||||||
bake(
|
bake(
|
||||||
self = self,
|
self=self,
|
||||||
mode = bake_mode,
|
mode=bake_mode,
|
||||||
size = bpy.context.scene.texToolsSettings.size,
|
size=bpy.context.scene.texToolsSettings.size,
|
||||||
|
|
||||||
bake_single = bpy.context.scene.texToolsSettings.bake_force_single,
|
bake_single=bpy.context.scene.texToolsSettings.bake_force_single,
|
||||||
sampling_scale = int(bpy.context.scene.texToolsSettings.bake_sampling),
|
sampling_scale=int(
|
||||||
samples = bpy.context.scene.texToolsSettings.bake_samples,
|
bpy.context.scene.texToolsSettings.bake_sampling),
|
||||||
ray_distance = bpy.context.scene.texToolsSettings.bake_ray_distance
|
samples=bpy.context.scene.texToolsSettings.bake_samples,
|
||||||
|
ray_distance=bpy.context.scene.texToolsSettings.bake_ray_distance
|
||||||
)
|
)
|
||||||
|
|
||||||
# Restore selection
|
# Restore selection
|
||||||
ub.restore_bake_settings()
|
ub.restore_bake_settings()
|
||||||
bpy.ops.object.select_all(action='DESELECT')
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
for obj in selected_objects:
|
for obj in selected_objects:
|
||||||
obj.select_set( state = True, view_layer = None)
|
obj.select_set(state=True, view_layer=None)
|
||||||
if active_object:
|
if active_object:
|
||||||
bpy.context.view_layer.objects.active = active_object
|
bpy.context.view_layer.objects.active = active_object
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def bake(self, mode, size, bake_single, sampling_scale, samples, ray_distance):
|
def bake(self, mode, size, bake_single, sampling_scale, samples, ray_distance):
|
||||||
|
|
||||||
print("Bake '{}'".format(mode))
|
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
|
# Disable edit mode
|
||||||
if bpy.context.view_layer.objects.active != None and bpy.context.object.mode != 'OBJECT':
|
if bpy.context.view_layer.objects.active != None and bpy.context.object.mode != 'OBJECT':
|
||||||
@ -103,29 +107,33 @@ def bake(self, mode, size, bake_single, sampling_scale, samples, ray_distance):
|
|||||||
render_width = sampling_scale * size[0]
|
render_width = sampling_scale * size[0]
|
||||||
render_height = sampling_scale * size[1]
|
render_height = sampling_scale * size[1]
|
||||||
|
|
||||||
for s in range(0,len(sets)):
|
for s in range(0, len(sets)):
|
||||||
set = sets[s]
|
set = sets[s]
|
||||||
|
|
||||||
# Get image name
|
# Get image name
|
||||||
name_texture = "{}_{}".format(set.name, mode)
|
name_texture = "{}_{}".format(set.name, mode)
|
||||||
if bake_single:
|
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))
|
path = bpy.path.abspath("//{}.tga".format(name_texture))
|
||||||
|
|
||||||
# Requires 1+ low poly objects
|
# Requires 1+ low poly objects
|
||||||
if len(set.objects_low) == 0:
|
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
|
return
|
||||||
|
|
||||||
# Check for UV maps
|
# Check for UV maps
|
||||||
for obj in set.objects_low:
|
for obj in set.objects_low:
|
||||||
if not obj.data.uv_layers or len(obj.data.uv_layers) == 0:
|
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
|
return
|
||||||
|
|
||||||
# Check for cage inconsistencies
|
# Check for cage inconsistencies
|
||||||
if len(set.objects_cage) > 0 and (len(set.objects_low) != len(set.objects_cage)):
|
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
|
return
|
||||||
|
|
||||||
# Get Materials
|
# Get Materials
|
||||||
@ -136,7 +144,6 @@ def bake(self, mode, size, bake_single, sampling_scale, samples, ray_distance):
|
|||||||
else:
|
else:
|
||||||
material_empty = bpy.data.materials.new(name="TT_bake_node")
|
material_empty = bpy.data.materials.new(name="TT_bake_node")
|
||||||
|
|
||||||
|
|
||||||
# Assign Materials to Objects
|
# Assign Materials to Objects
|
||||||
if (len(set.objects_high) + len(set.objects_float)) == 0:
|
if (len(set.objects_high) + len(set.objects_float)) == 0:
|
||||||
# Low poly bake: Assign material to lowpoly
|
# Low poly bake: Assign material to lowpoly
|
||||||
@ -153,14 +160,13 @@ def bake(self, mode, size, bake_single, sampling_scale, samples, ray_distance):
|
|||||||
assign_vertex_color(mode, obj)
|
assign_vertex_color(mode, obj)
|
||||||
assign_material(mode, obj, material_loaded)
|
assign_material(mode, obj, material_loaded)
|
||||||
|
|
||||||
|
|
||||||
# Setup Image
|
# Setup Image
|
||||||
is_clear = (not bake_single) or (bake_single and s==0)
|
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
|
# Assign bake node to Material
|
||||||
setup_image_bake_node(set.objects_low[0], image)
|
setup_image_bake_node(set.objects_low[0], image)
|
||||||
|
|
||||||
|
|
||||||
print("Bake '{}' = {}".format(set.name, path))
|
print("Bake '{}' = {}".format(set.name, path))
|
||||||
|
|
||||||
@ -171,13 +177,14 @@ def bake(self, mode, size, bake_single, sampling_scale, samples, ray_distance):
|
|||||||
# Bake each low poly object in this set
|
# Bake each low poly object in this set
|
||||||
for i in range(len(set.objects_low)):
|
for i in range(len(set.objects_low)):
|
||||||
obj_low = set.objects_low[i]
|
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
|
# Disable hide render
|
||||||
obj_low.hide_render = False
|
obj_low.hide_render = False
|
||||||
|
|
||||||
bpy.ops.object.select_all(action='DESELECT')
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
obj_low.select_set( state = True, view_layer = None)
|
obj_low.select_set(state=True, view_layer=None)
|
||||||
bpy.context.view_layer.objects.active = obj_low
|
bpy.context.view_layer.objects.active = obj_low
|
||||||
|
|
||||||
if modes[mode].engine == 'BLENDER_EEVEE':
|
if modes[mode].engine == 'BLENDER_EEVEE':
|
||||||
@ -190,34 +197,33 @@ def bake(self, mode, size, bake_single, sampling_scale, samples, ray_distance):
|
|||||||
area.spaces[0].image = image
|
area.spaces[0].image = image
|
||||||
# bpy.data.screens['UV Editing'].areas[1].spaces[0].image = image
|
# bpy.data.screens['UV Editing'].areas[1].spaces[0].image = image
|
||||||
|
|
||||||
|
|
||||||
bpy.ops.object.mode_set(mode='OBJECT')
|
bpy.ops.object.mode_set(mode='OBJECT')
|
||||||
|
|
||||||
for obj_high in (set.objects_high):
|
for obj_high in (set.objects_high):
|
||||||
obj_high.select_set( state = True, view_layer = None)
|
obj_high.select_set(state=True, view_layer=None)
|
||||||
cycles_bake(
|
cycles_bake(
|
||||||
mode,
|
mode,
|
||||||
bpy.context.scene.texToolsSettings.padding,
|
bpy.context.scene.texToolsSettings.padding,
|
||||||
sampling_scale,
|
sampling_scale,
|
||||||
samples,
|
samples,
|
||||||
ray_distance,
|
ray_distance,
|
||||||
len(set.objects_high) > 0,
|
len(set.objects_high) > 0,
|
||||||
obj_cage
|
obj_cage
|
||||||
)
|
)
|
||||||
|
|
||||||
# Bake Floaters seperate bake
|
# Bake Floaters seperate bake
|
||||||
if len(set.objects_float) > 0:
|
if len(set.objects_float) > 0:
|
||||||
bpy.ops.object.select_all(action='DESELECT')
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
for obj_high in (set.objects_float):
|
for obj_high in (set.objects_float):
|
||||||
obj_high.select_set( state = True, view_layer = None)
|
obj_high.select_set(state=True, view_layer=None)
|
||||||
obj_low.select_set( state = True, view_layer = None)
|
obj_low.select_set(state=True, view_layer=None)
|
||||||
|
|
||||||
cycles_bake(
|
cycles_bake(
|
||||||
mode,
|
mode,
|
||||||
0,
|
0,
|
||||||
sampling_scale,
|
sampling_scale,
|
||||||
samples,
|
samples,
|
||||||
ray_distance,
|
ray_distance,
|
||||||
len(set.objects_float) > 0,
|
len(set.objects_float) > 0,
|
||||||
obj_cage
|
obj_cage
|
||||||
)
|
)
|
||||||
@ -231,16 +237,16 @@ def bake(self, mode, size, bake_single, sampling_scale, samples, ray_distance):
|
|||||||
for obj_cage in set.objects_cage:
|
for obj_cage in set.objects_cage:
|
||||||
obj_cage.hide_render = False
|
obj_cage.hide_render = False
|
||||||
|
|
||||||
|
|
||||||
# Downsample image?
|
# Downsample image?
|
||||||
if not bake_single or (bake_single and s == len(sets)-1):
|
if not bake_single or (bake_single and s == len(sets)-1):
|
||||||
# When baking single, only downsample on last bake
|
# When baking single, only downsample on last bake
|
||||||
if render_width != size[0] or render_height != size[1]:
|
if render_width != size[0] or render_height != size[1]:
|
||||||
image.scale(size[0],size[1])
|
image.scale(size[0], size[1])
|
||||||
|
|
||||||
# Apply composite nodes on final image result
|
# Apply composite nodes on final image result
|
||||||
if modes[mode].composite:
|
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()
|
# image.save()
|
||||||
|
|
||||||
@ -248,8 +254,6 @@ def bake(self, mode, size, bake_single, sampling_scale, samples, ray_distance):
|
|||||||
ub.restore_materials()
|
ub.restore_materials()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def apply_composite(image, scene_name, size):
|
def apply_composite(image, scene_name, size):
|
||||||
previous_scene = bpy.context.window.scene
|
previous_scene = bpy.context.window.scene
|
||||||
|
|
||||||
@ -258,31 +262,33 @@ def apply_composite(image, scene_name, size):
|
|||||||
if scene_name in bpy.data.scenes:
|
if scene_name in bpy.data.scenes:
|
||||||
scene = bpy.data.scenes[scene_name]
|
scene = bpy.data.scenes[scene_name]
|
||||||
else:
|
else:
|
||||||
path = os.path.join(os.path.dirname(__file__), "resources/compositing.blend")+"\\Scene\\"
|
path = os.path.join(os.path.dirname(__file__),
|
||||||
bpy.ops.wm.append(filename=scene_name, directory=path, link=False, autoselect=False)
|
"resources/compositing.blend")+"\\Scene\\"
|
||||||
|
bpy.ops.wm.append(filename=scene_name, directory=path,
|
||||||
|
link=False, autoselect=False)
|
||||||
scene = bpy.data.scenes[scene_name]
|
scene = bpy.data.scenes[scene_name]
|
||||||
|
|
||||||
if scene:
|
if scene:
|
||||||
# Switch scene
|
# Switch scene
|
||||||
bpy.context.window.scene = scene
|
bpy.context.window.scene = scene
|
||||||
|
|
||||||
#Setup composite nodes for Curvature
|
# Setup composite nodes for Curvature
|
||||||
if "Image" in scene.node_tree.nodes:
|
if "Image" in scene.node_tree.nodes:
|
||||||
scene.node_tree.nodes["Image"].image = image
|
scene.node_tree.nodes["Image"].image = image
|
||||||
|
|
||||||
if "Offset" in scene.node_tree.nodes:
|
if "Offset" in scene.node_tree.nodes:
|
||||||
scene.node_tree.nodes["Offset"].outputs[0].default_value = size
|
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
|
# Render image
|
||||||
bpy.ops.render.render(use_viewport=False)
|
bpy.ops.render.render(use_viewport=False)
|
||||||
|
|
||||||
|
|
||||||
# Get last images of viewer node and render result
|
# Get last images of viewer node and render result
|
||||||
image_viewer_node = get_last_item("Viewer Node", bpy.data.images)
|
image_viewer_node = get_last_item("Viewer Node", bpy.data.images)
|
||||||
image_render_result = get_last_item("Render Result", bpy.data.images)
|
image_render_result = get_last_item("Render Result", bpy.data.images)
|
||||||
|
|
||||||
#Copy pixels
|
# Copy pixels
|
||||||
image.pixels = image_viewer_node.pixels[:]
|
image.pixels = image_viewer_node.pixels[:]
|
||||||
image.update()
|
image.update()
|
||||||
|
|
||||||
@ -291,14 +297,13 @@ def apply_composite(image, scene_name, size):
|
|||||||
if image_render_result:
|
if image_render_result:
|
||||||
bpy.data.images.remove(image_render_result)
|
bpy.data.images.remove(image_render_result)
|
||||||
|
|
||||||
#Restore scene & remove other scene
|
# Restore scene & remove other scene
|
||||||
bpy.context.window.scene = previous_scene
|
bpy.context.window.scene = previous_scene
|
||||||
|
|
||||||
# Delete compositing scene
|
# Delete compositing scene
|
||||||
bpy.data.scenes.remove(scene)
|
bpy.data.scenes.remove(scene)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_last_item(key_name, collection):
|
def get_last_item(key_name, collection):
|
||||||
# bpy.data.images
|
# bpy.data.images
|
||||||
# Get last image of a series, e.g. .001, .002, 003
|
# Get last image of a series, e.g. .001, .002, 003
|
||||||
@ -307,7 +312,7 @@ def get_last_item(key_name, collection):
|
|||||||
if key_name in item.name:
|
if key_name in item.name:
|
||||||
keys.append(item.name)
|
keys.append(item.name)
|
||||||
|
|
||||||
print("Search for {}x : '{}'".format(len(keys), ",".join(keys) ) )
|
print("Search for {}x : '{}'".format(len(keys), ",".join(keys)))
|
||||||
|
|
||||||
if len(keys) > 0:
|
if len(keys) > 0:
|
||||||
return collection[keys[-1]]
|
return collection[keys[-1]]
|
||||||
@ -315,8 +320,6 @@ def get_last_item(key_name, collection):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def setup_image(mode, name, width, height, path, is_clear):
|
def setup_image(mode, name, width, height, path, is_clear):
|
||||||
image = None
|
image = None
|
||||||
|
|
||||||
@ -331,7 +334,6 @@ def setup_image(mode, name, width, height, path, is_clear):
|
|||||||
# bpy.data.images[name].update()
|
# bpy.data.images[name].update()
|
||||||
|
|
||||||
# if bpy.data.images[name].has_data == False:
|
# if bpy.data.images[name].has_data == False:
|
||||||
|
|
||||||
|
|
||||||
# Previous image does not have data, remove first
|
# Previous image does not have data, remove first
|
||||||
# print("Image pointer exists but no data "+name)
|
# print("Image pointer exists but no data "+name)
|
||||||
@ -343,13 +345,13 @@ def setup_image(mode, name, width, height, path, is_clear):
|
|||||||
if name not in bpy.data.images:
|
if name not in bpy.data.images:
|
||||||
# Create new image with 32 bit float
|
# Create new image with 32 bit float
|
||||||
is_float_32 = bpy.context.preferences.addons["textools"].preferences.bake_32bit_float == '32'
|
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:
|
if "_normal_" in image.name:
|
||||||
image.colorspace_settings.name = 'Non-Color'
|
image.colorspace_settings.name = 'Non-Color'
|
||||||
else:
|
else:
|
||||||
image.colorspace_settings.name = 'sRGB'
|
image.colorspace_settings.name = 'sRGB'
|
||||||
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Reuse existing Image
|
# Reuse existing Image
|
||||||
image = bpy.data.images[name]
|
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_color = modes[mode].color
|
||||||
image.generated_type = 'BLANK'
|
image.generated_type = 'BLANK'
|
||||||
|
|
||||||
|
|
||||||
image.file_format = 'TARGA'
|
image.file_format = 'TARGA'
|
||||||
|
|
||||||
# TODO: Verify that the path exists
|
# TODO: Verify that the path exists
|
||||||
@ -373,11 +374,10 @@ def setup_image(mode, name, width, height, path, is_clear):
|
|||||||
return image
|
return image
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def setup_image_bake_node(obj, image):
|
def setup_image_bake_node(obj, image):
|
||||||
|
|
||||||
if len(obj.data.materials) <= 0:
|
if len(obj.data.materials) <= 0:
|
||||||
print("ERROR, need spare material to setup active image texture to bake!!!")
|
print("ERROR, need spare material to setup active image texture to bake!!!")
|
||||||
else:
|
else:
|
||||||
for slot in obj.material_slots:
|
for slot in obj.material_slots:
|
||||||
if slot.material:
|
if slot.material:
|
||||||
@ -397,46 +397,45 @@ def setup_image_bake_node(obj, image):
|
|||||||
tree.nodes.active = node
|
tree.nodes.active = node
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def assign_vertex_color(mode, obj):
|
def assign_vertex_color(mode, obj):
|
||||||
if modes[mode].setVColor:
|
if modes[mode].setVColor:
|
||||||
modes[mode].setVColor(obj)
|
modes[mode].setVColor(obj)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def assign_material(mode, obj, material_bake=None, material_empty=None):
|
def assign_material(mode, obj, material_bake=None, material_empty=None):
|
||||||
ub.store_materials(obj)
|
ub.store_materials(obj)
|
||||||
|
|
||||||
bpy.context.view_layer.objects.active = obj
|
bpy.context.view_layer.objects.active = obj
|
||||||
obj.select_set( state = True, view_layer = None)
|
obj.select_set(state=True, view_layer=None)
|
||||||
|
|
||||||
# Select All faces
|
# Select All faces
|
||||||
bpy.ops.object.mode_set(mode='EDIT')
|
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]
|
faces = [face for face in bm.faces if face.select]
|
||||||
bpy.ops.mesh.select_all(action='SELECT')
|
bpy.ops.mesh.select_all(action='SELECT')
|
||||||
|
|
||||||
|
|
||||||
if material_bake:
|
if material_bake:
|
||||||
# Setup properties of bake materials
|
# Setup properties of bake materials
|
||||||
if mode == 'wireframe':
|
if mode == 'wireframe':
|
||||||
if "Value" in material_bake.node_tree.nodes:
|
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 mode == 'bevel_mask':
|
||||||
if "Bevel" in material_bake.node_tree.nodes:
|
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
|
material_bake.node_tree.nodes["Bevel"].samples = bpy.context.scene.texToolsSettings.bake_bevel_samples
|
||||||
if mode == 'normal_tangent_bevel':
|
if mode == 'normal_tangent_bevel':
|
||||||
if "Bevel" in material_bake.node_tree.nodes:
|
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
|
material_bake.node_tree.nodes["Bevel"].samples = bpy.context.scene.texToolsSettings.bake_bevel_samples
|
||||||
if mode == 'normal_object_bevel':
|
if mode == 'normal_object_bevel':
|
||||||
if "Bevel" in material_bake.node_tree.nodes:
|
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
|
material_bake.node_tree.nodes["Bevel"].samples = bpy.context.scene.texToolsSettings.bake_bevel_samples
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Don't apply in diffuse mode
|
# Don't apply in diffuse mode
|
||||||
if mode != 'diffuse':
|
if mode != 'diffuse':
|
||||||
if material_bake:
|
if material_bake:
|
||||||
@ -450,11 +449,11 @@ def assign_material(mode, obj, material_bake=None, material_empty=None):
|
|||||||
bpy.ops.object.material_slot_assign()
|
bpy.ops.object.material_slot_assign()
|
||||||
|
|
||||||
elif material_empty:
|
elif material_empty:
|
||||||
#Assign material_empty if no material available
|
# Assign material_empty if no material available
|
||||||
if len(obj.material_slots) == 0:
|
if len(obj.material_slots) == 0:
|
||||||
obj.data.materials.append(material_empty)
|
obj.data.materials.append(material_empty)
|
||||||
|
|
||||||
else: # not obj.material_slots[0].material:
|
else: # not obj.material_slots[0].material:
|
||||||
obj.material_slots[0].material = material_empty
|
obj.material_slots[0].material = material_empty
|
||||||
obj.active_material_index = 0
|
obj.active_material_index = 0
|
||||||
bpy.ops.object.material_slot_assign()
|
bpy.ops.object.material_slot_assign()
|
||||||
@ -466,40 +465,33 @@ def assign_material(mode, obj, material_bake=None, material_empty=None):
|
|||||||
|
|
||||||
bpy.ops.object.mode_set(mode='OBJECT')
|
bpy.ops.object.mode_set(mode='OBJECT')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_material(mode):
|
def get_material(mode):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if modes[mode].material == "":
|
if modes[mode].material == "":
|
||||||
return None # No material setup requires
|
return None # No material setup requires
|
||||||
|
|
||||||
# Find or load material
|
# Find or load material
|
||||||
name = modes[mode].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:
|
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))
|
print("Get mat {}\n{}".format(mode, path))
|
||||||
|
|
||||||
if bpy.data.materials.get(name) is None:
|
if bpy.data.materials.get(name) is None:
|
||||||
print("Material not yet loaded: "+mode)
|
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)
|
return bpy.data.materials.get(name)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def cycles_bake(mode, padding, sampling_scale, samples, ray_distance, is_multi, obj_cage):
|
def cycles_bake(mode, padding, sampling_scale, samples, ray_distance, is_multi, obj_cage):
|
||||||
|
|
||||||
|
|
||||||
# if modes[mode].engine == 'BLENDER_EEVEE':
|
# if modes[mode].engine == 'BLENDER_EEVEE':
|
||||||
# # Snippet: https://gist.github.com/AndrewRayCode/760c4634a77551827de41ed67585064b
|
# # Snippet: https://gist.github.com/AndrewRayCode/760c4634a77551827de41ed67585064b
|
||||||
# bpy.context.scene.render.bake_margin = padding
|
# bpy.context.scene.render.bake_margin = padding
|
||||||
|
|
||||||
@ -518,11 +510,10 @@ def cycles_bake(mode, padding, sampling_scale, samples, ray_distance, is_multi,
|
|||||||
|
|
||||||
# bpy.ops.object.bake_image()
|
# bpy.ops.object.bake_image()
|
||||||
|
|
||||||
|
if modes[mode].engine == 'CYCLES' or modes[mode].engine == 'BLENDER_EEVEE':
|
||||||
if modes[mode].engine == 'CYCLES' or modes[mode].engine == 'BLENDER_EEVEE' :
|
|
||||||
|
|
||||||
if modes[mode].normal_space == 'OBJECT':
|
if modes[mode].normal_space == 'OBJECT':
|
||||||
#See: https://twitter.com/Linko_3D/status/963066705584054272
|
# See: https://twitter.com/Linko_3D/status/963066705584054272
|
||||||
bpy.context.scene.render.bake.normal_r = 'POS_X'
|
bpy.context.scene.render.bake.normal_r = 'POS_X'
|
||||||
bpy.context.scene.render.bake.normal_g = 'POS_Z'
|
bpy.context.scene.render.bake.normal_g = 'POS_Z'
|
||||||
bpy.context.scene.render.bake.normal_b = 'NEG_Y'
|
bpy.context.scene.render.bake.normal_b = 'NEG_Y'
|
||||||
@ -556,26 +547,37 @@ def cycles_bake(mode, padding, sampling_scale, samples, ray_distance, is_multi,
|
|||||||
if obj_cage is None:
|
if obj_cage is None:
|
||||||
# Bake with Cage
|
# Bake with Cage
|
||||||
bpy.ops.object.bake(
|
bpy.ops.object.bake(
|
||||||
type=modes[mode].type,
|
type=modes[mode].type,
|
||||||
use_clear=False,
|
use_clear=False,
|
||||||
cage_extrusion=ray_distance,
|
cage_extrusion=ray_distance,
|
||||||
|
|
||||||
use_selected_to_active=is_multi,
|
use_selected_to_active=is_multi,
|
||||||
normal_space=modes[mode].normal_space
|
normal_space=modes[mode].normal_space
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# Bake without Cage
|
# Bake without Cage
|
||||||
bpy.ops.object.bake(
|
bpy.ops.object.bake(
|
||||||
type=modes[mode].type,
|
type=modes[mode].type,
|
||||||
use_clear=False,
|
use_clear=False,
|
||||||
cage_extrusion=ray_distance,
|
cage_extrusion=ray_distance,
|
||||||
|
|
||||||
use_selected_to_active=is_multi,
|
use_selected_to_active=is_multi,
|
||||||
normal_space=modes[mode].normal_space,
|
normal_space=modes[mode].normal_space,
|
||||||
|
|
||||||
#Use Cage and assign object
|
# Use Cage and assign object
|
||||||
use_cage=True,
|
use_cage=True,
|
||||||
cage_object=obj_cage.name
|
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)
|
bpy.utils.register_class(op)
|
||||||
|
@ -23,15 +23,12 @@ class op(bpy.types.Operator):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
explode(self)
|
explode(self)
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def explode(self):
|
def explode(self):
|
||||||
sets = settings.sets
|
sets = settings.sets
|
||||||
|
|
||||||
@ -40,13 +37,14 @@ def explode(self):
|
|||||||
avg_side = 0
|
avg_side = 0
|
||||||
for set in sets:
|
for set in sets:
|
||||||
set_bounds[set] = get_bbox_set(set)
|
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'].x
|
||||||
avg_side+=set_bounds[set]['size'].y
|
avg_side += set_bounds[set]['size'].y
|
||||||
avg_side+=set_bounds[set]['size'].z
|
avg_side += set_bounds[set]['size'].z
|
||||||
|
|
||||||
avg_side/=(len(sets)*3)
|
avg_side /= (len(sets)*3)
|
||||||
|
|
||||||
sorted_set_volume = sorted(set_volume.items(), key=operator.itemgetter(1))
|
sorted_set_volume = sorted(set_volume.items(), key=operator.itemgetter(1))
|
||||||
sorted_sets = [item[0] for item in sorted_set_volume]
|
sorted_sets = [item[0] for item in sorted_set_volume]
|
||||||
@ -54,26 +52,23 @@ def explode(self):
|
|||||||
|
|
||||||
# All combined bounding boxes
|
# All combined bounding boxes
|
||||||
bbox_all = merge_bounds(list(set_bounds.values()))
|
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
|
# Offset sets into their direction
|
||||||
dir_offset_last_bbox = {}
|
dir_offset_last_bbox = {}
|
||||||
for i in range(0,6):
|
for i in range(0, 6):
|
||||||
dir_offset_last_bbox[i] = bbox_max #bbox_all
|
dir_offset_last_bbox[i] = bbox_max # bbox_all
|
||||||
|
|
||||||
|
|
||||||
bpy.context.scene.frame_start = 0
|
bpy.context.scene.frame_start = 0
|
||||||
bpy.context.scene.frame_end = frame_range
|
bpy.context.scene.frame_end = frame_range
|
||||||
bpy.context.scene.frame_current = 0
|
bpy.context.scene.frame_current = 0
|
||||||
|
|
||||||
|
|
||||||
# Process each set
|
# Process each set
|
||||||
for set in sorted_sets:
|
for set in sorted_sets:
|
||||||
if set_bounds[set] != bbox_max:
|
if set_bounds[set] != bbox_max:
|
||||||
delta = set_bounds[set]['center'] - bbox_all['center']
|
delta = set_bounds[set]['center'] - bbox_all['center']
|
||||||
offset_set(set, delta, avg_side*0.35, dir_offset_last_bbox )
|
offset_set(set, delta, avg_side*0.35, dir_offset_last_bbox)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def offset_set(set, delta, margin, dir_offset_last_bbox):
|
def offset_set(set, delta, margin, dir_offset_last_bbox):
|
||||||
@ -82,16 +77,16 @@ def offset_set(set, delta, margin, dir_offset_last_bbox):
|
|||||||
|
|
||||||
# Which Direction?
|
# Which Direction?
|
||||||
delta_max = max(abs(delta.x), abs(delta.y), abs(delta.z))
|
delta_max = max(abs(delta.x), abs(delta.y), abs(delta.z))
|
||||||
direction = [0,0,0]
|
direction = [0, 0, 0]
|
||||||
if delta_max > 0:
|
if delta_max > 0:
|
||||||
for i in range(0,3):
|
for i in range(0, 3):
|
||||||
if abs(delta[i]) == delta_max:
|
if abs(delta[i]) == delta_max:
|
||||||
direction[i] = delta[i]/abs(delta[i])
|
direction[i] = delta[i]/abs(delta[i])
|
||||||
else:
|
else:
|
||||||
direction[i] = 0
|
direction[i] = 0
|
||||||
else:
|
else:
|
||||||
# Default when not delta offset was measure move up
|
# Default when not delta offset was measure move up
|
||||||
direction = [0,0,1]
|
direction = [0, 0, 1]
|
||||||
|
|
||||||
delta = Vector((direction[0], direction[1], direction[2]))
|
delta = Vector((direction[0], direction[1], direction[2]))
|
||||||
|
|
||||||
@ -101,26 +96,26 @@ def offset_set(set, delta, margin, dir_offset_last_bbox):
|
|||||||
# Calculate Offset
|
# Calculate Offset
|
||||||
bbox = get_bbox_set(set)
|
bbox = get_bbox_set(set)
|
||||||
bbox_last = dir_offset_last_bbox[key]
|
bbox_last = dir_offset_last_bbox[key]
|
||||||
|
|
||||||
offset = Vector((0,0,0))
|
offset = Vector((0, 0, 0))
|
||||||
|
|
||||||
if delta.x == 1:
|
if delta.x == 1:
|
||||||
offset = delta * ( bbox_last['max'].x - bbox['min'].x )
|
offset = delta * (bbox_last['max'].x - bbox['min'].x)
|
||||||
elif delta.x == -1:
|
elif delta.x == -1:
|
||||||
offset = delta * -( bbox_last['min'].x - bbox['max'].x )
|
offset = delta * -(bbox_last['min'].x - bbox['max'].x)
|
||||||
|
|
||||||
elif delta.y == 1:
|
elif delta.y == 1:
|
||||||
offset = delta * ( bbox_last['max'].y - bbox['min'].y )
|
offset = delta * (bbox_last['max'].y - bbox['min'].y)
|
||||||
elif delta.y == -1:
|
elif delta.y == -1:
|
||||||
offset = delta * -( bbox_last['min'].y - bbox['max'].y )
|
offset = delta * -(bbox_last['min'].y - bbox['max'].y)
|
||||||
|
|
||||||
elif delta.z == 1:
|
elif delta.z == 1:
|
||||||
offset = delta * ( bbox_last['max'].z - bbox['min'].z )
|
offset = delta * (bbox_last['max'].z - bbox['min'].z)
|
||||||
elif delta.z == -1:
|
elif delta.z == -1:
|
||||||
offset = delta * -( bbox_last['min'].z - bbox['max'].z )
|
offset = delta * -(bbox_last['min'].z - bbox['max'].z)
|
||||||
|
|
||||||
# Add margin
|
# Add margin
|
||||||
offset+= delta * margin
|
offset += delta * margin
|
||||||
|
|
||||||
# Offset items
|
# Offset items
|
||||||
# https://blenderartists.org/forum/showthread.php?237761-Blender-2-6-Set-keyframes-using-Python-script
|
# https://blenderartists.org/forum/showthread.php?237761-Blender-2-6-Set-keyframes-using-Python-script
|
||||||
@ -144,8 +139,6 @@ def offset_set(set, delta, margin, dir_offset_last_bbox):
|
|||||||
dir_offset_last_bbox[key] = get_bbox_set(set)
|
dir_offset_last_bbox[key] = get_bbox_set(set)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_delta_key(delta):
|
def get_delta_key(delta):
|
||||||
# print("Get key {} is: {}".format(delta, delta.y == -1 ))
|
# print("Get key {} is: {}".format(delta, delta.y == -1 ))
|
||||||
if delta.x == -1:
|
if delta.x == -1:
|
||||||
@ -162,39 +155,36 @@ def get_delta_key(delta):
|
|||||||
return 5
|
return 5
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def merge_bounds(bounds):
|
def merge_bounds(bounds):
|
||||||
box_min = bounds[0]['min'].copy()
|
box_min = bounds[0]['min'].copy()
|
||||||
box_max = bounds[0]['max'].copy()
|
box_max = bounds[0]['max'].copy()
|
||||||
|
|
||||||
for bbox in bounds:
|
for bbox in bounds:
|
||||||
# box_min.x = -8
|
# box_min.x = -8
|
||||||
box_min.x = min(box_min.x, bbox['min'].x)
|
box_min.x = min(box_min.x, bbox['min'].x)
|
||||||
box_min.y = min(box_min.y, bbox['min'].y)
|
box_min.y = min(box_min.y, bbox['min'].y)
|
||||||
box_min.z = min(box_min.z, bbox['min'].z)
|
box_min.z = min(box_min.z, bbox['min'].z)
|
||||||
|
|
||||||
box_max.x = max(box_max.x, bbox['max'].x)
|
box_max.x = max(box_max.x, bbox['max'].x)
|
||||||
box_max.y = max(box_max.y, bbox['max'].y)
|
box_max.y = max(box_max.y, bbox['max'].y)
|
||||||
box_max.z = max(box_max.z, bbox['max'].z)
|
box_max.z = max(box_max.z, bbox['max'].z)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'min':box_min,
|
'min': box_min,
|
||||||
'max':box_max,
|
'max': box_max,
|
||||||
'size':(box_max-box_min),
|
'size': (box_max-box_min),
|
||||||
'center':box_min+(box_max-box_min)/2
|
'center': box_min+(box_max-box_min)/2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_bbox_set(set):
|
def get_bbox_set(set):
|
||||||
objects = set.objects_low + set.objects_high + set.objects_cage
|
objects = set.objects_low + set.objects_high + set.objects_cage
|
||||||
bounds = []
|
bounds = []
|
||||||
for obj in objects:
|
for obj in objects:
|
||||||
bounds.append( get_bbox(obj) )
|
bounds.append(get_bbox(obj))
|
||||||
return merge_bounds(bounds)
|
return merge_bounds(bounds)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_bbox(obj):
|
def get_bbox(obj):
|
||||||
corners = [obj.matrix_world @ Vector(corner) for corner in obj.bound_box]
|
corners = [obj.matrix_world @ Vector(corner) for corner in obj.bound_box]
|
||||||
|
|
||||||
@ -206,16 +196,17 @@ def get_bbox(obj):
|
|||||||
box_min.x = min(box_min.x, corner.x)
|
box_min.x = min(box_min.x, corner.x)
|
||||||
box_min.y = min(box_min.y, corner.y)
|
box_min.y = min(box_min.y, corner.y)
|
||||||
box_min.z = min(box_min.z, corner.z)
|
box_min.z = min(box_min.z, corner.z)
|
||||||
|
|
||||||
box_max.x = max(box_max.x, corner.x)
|
box_max.x = max(box_max.x, corner.x)
|
||||||
box_max.y = max(box_max.y, corner.y)
|
box_max.y = max(box_max.y, corner.y)
|
||||||
box_max.z = max(box_max.z, corner.z)
|
box_max.z = max(box_max.z, corner.z)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'min':box_min,
|
'min': box_min,
|
||||||
'max':box_max,
|
'max': box_max,
|
||||||
'size':(box_max-box_min),
|
'size': (box_max-box_min),
|
||||||
'center':box_min+(box_max-box_min)/2
|
'center': box_min+(box_max-box_min)/2
|
||||||
}
|
}
|
||||||
|
|
||||||
bpy.utils.register_class(op)
|
|
||||||
|
bpy.utils.register_class(op)
|
||||||
|
@ -7,25 +7,25 @@ from math import pi
|
|||||||
|
|
||||||
from . import utilities_color
|
from . import utilities_color
|
||||||
|
|
||||||
|
|
||||||
class op(bpy.types.Operator):
|
class op(bpy.types.Operator):
|
||||||
bl_idname = "uv.textools_color_from_directions"
|
bl_idname = "uv.textools_color_from_directions"
|
||||||
bl_label = "Color Directions"
|
bl_label = "Color Directions"
|
||||||
bl_description = "Assign a color ID to different face directions"
|
bl_description = "Assign a color ID to different face directions"
|
||||||
bl_options = {'REGISTER', 'UNDO'}
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
directions : bpy.props.EnumProperty(items=
|
directions: bpy.props.EnumProperty(items=[('2', '2', 'Top & Bottom, Sides'),
|
||||||
[('2', '2', 'Top & Bottom, Sides'),
|
('3', '3', 'Top & Bottom, Left & Right, Front & Back'),
|
||||||
('3', '3', 'Top & Bottom, Left & Right, Front & Back'),
|
('4', '4', 'Top, Left & Right, Front & Back, Bottom'),
|
||||||
('4', '4', 'Top, Left & Right, Front & Back, Bottom'),
|
('6', '6', 'All sides')],
|
||||||
('6', '6', 'All sides')],
|
name="Directions",
|
||||||
name = "Directions",
|
default='3'
|
||||||
default = '3'
|
)
|
||||||
)
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
wm = context.window_manager
|
wm = context.window_manager
|
||||||
return wm.invoke_props_dialog(self)
|
return wm.invoke_props_dialog(self)
|
||||||
|
|
||||||
|
|
||||||
# def draw(self, context):
|
# def draw(self, context):
|
||||||
# layout = self.layout
|
# layout = self.layout
|
||||||
# layout.prop(self, "directions")
|
# layout.prop(self, "directions")
|
||||||
@ -44,43 +44,39 @@ class op(bpy.types.Operator):
|
|||||||
if bpy.context.active_object.type != 'MESH':
|
if bpy.context.active_object.type != 'MESH':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#Only in UV editor mode
|
# Only in UV editor mode
|
||||||
if bpy.context.area.type != 'IMAGE_EDITOR':
|
if bpy.context.area.type != 'IMAGE_EDITOR':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
color_elements(self, context)
|
color_elements(self, context)
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def color_elements(self, context):
|
def color_elements(self, context):
|
||||||
obj = bpy.context.active_object
|
obj = bpy.context.active_object
|
||||||
|
|
||||||
# Setup Edit & Face mode
|
# Setup Edit & Face mode
|
||||||
if obj.mode != 'EDIT':
|
if obj.mode != 'EDIT':
|
||||||
bpy.ops.object.mode_set(mode='EDIT')
|
bpy.ops.object.mode_set(mode='EDIT')
|
||||||
bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE')
|
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);
|
|
||||||
|
|
||||||
|
# Collect groups
|
||||||
|
bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
|
||||||
|
|
||||||
face_directions = {
|
face_directions = {
|
||||||
'top':[],
|
'top': [],
|
||||||
'bottom':[],
|
'bottom': [],
|
||||||
'left':[],
|
'left': [],
|
||||||
'right':[],
|
'right': [],
|
||||||
'front':[],
|
'front': [],
|
||||||
'back':[]
|
'back': []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
print("Directions {}".format(self.directions))
|
print("Directions {}".format(self.directions))
|
||||||
|
|
||||||
|
|
||||||
for face in bm.faces:
|
for face in bm.faces:
|
||||||
print("face {} n: {}".format(face.index, face.normal))
|
print("face {} n: {}".format(face.index, face.normal))
|
||||||
# Find dominant direction
|
# Find dominant direction
|
||||||
@ -114,7 +110,8 @@ def color_elements(self, context):
|
|||||||
|
|
||||||
if self.directions == '2':
|
if self.directions == '2':
|
||||||
groups.append(face_directions['top']+face_directions['bottom'])
|
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':
|
if self.directions == '3':
|
||||||
groups.append(face_directions['top']+face_directions['bottom'])
|
groups.append(face_directions['top']+face_directions['bottom'])
|
||||||
groups.append(face_directions['left']+face_directions['right'])
|
groups.append(face_directions['left']+face_directions['right'])
|
||||||
@ -136,8 +133,8 @@ def color_elements(self, context):
|
|||||||
index_color = 0
|
index_color = 0
|
||||||
for group in groups:
|
for group in groups:
|
||||||
# # rebuild bmesh data (e.g. left edit mode previous loop)
|
# # 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"):
|
if hasattr(bm.faces, "ensure_lookup_table"):
|
||||||
bm.faces.ensure_lookup_table()
|
bm.faces.ensure_lookup_table()
|
||||||
|
|
||||||
# Select group
|
# Select group
|
||||||
@ -148,7 +145,8 @@ def color_elements(self, context):
|
|||||||
# Assign to selection
|
# Assign to selection
|
||||||
bpy.ops.uv.textools_color_assign(index=index_color)
|
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')
|
bpy.ops.object.mode_set(mode='OBJECT')
|
||||||
utilities_color.validate_face_colors(obj)
|
utilities_color.validate_face_colors(obj)
|
||||||
@ -203,4 +201,5 @@ def color_elements(self, context):
|
|||||||
utilities_color.validate_face_colors(obj)
|
utilities_color.validate_face_colors(obj)
|
||||||
'''
|
'''
|
||||||
|
|
||||||
bpy.utils.register_class(op)
|
|
||||||
|
bpy.utils.register_class(op)
|
||||||
|
@ -7,12 +7,12 @@ from math import pi
|
|||||||
|
|
||||||
from . import utilities_color
|
from . import utilities_color
|
||||||
|
|
||||||
|
|
||||||
class op(bpy.types.Operator):
|
class op(bpy.types.Operator):
|
||||||
bl_idname = "uv.textools_color_from_materials"
|
bl_idname = "uv.textools_color_from_materials"
|
||||||
bl_label = "Color Elements"
|
bl_label = "Color Elements"
|
||||||
bl_description = "Assign a color ID to each mesh material slot"
|
bl_description = "Assign a color ID to each mesh material slot"
|
||||||
bl_options = {'REGISTER', 'UNDO'}
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
@ -28,21 +28,20 @@ class op(bpy.types.Operator):
|
|||||||
if bpy.context.active_object.type != 'MESH':
|
if bpy.context.active_object.type != 'MESH':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#Only in UV editor mode
|
# Only in UV editor mode
|
||||||
if bpy.context.area.type != 'IMAGE_EDITOR':
|
if bpy.context.area.type != 'IMAGE_EDITOR':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
color_materials(self, context)
|
color_materials(self, context)
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def color_materials(self, context):
|
def color_materials(self, context):
|
||||||
obj = bpy.context.active_object
|
obj = bpy.context.active_object
|
||||||
|
|
||||||
for s in range(len(obj.material_slots)):
|
for s in range(len(obj.material_slots)):
|
||||||
slot = obj.material_slots[s]
|
slot = obj.material_slots[s]
|
||||||
if slot.material:
|
if slot.material:
|
||||||
@ -50,4 +49,5 @@ def color_materials(self, context):
|
|||||||
|
|
||||||
utilities_color.validate_face_colors(obj)
|
utilities_color.validate_face_colors(obj)
|
||||||
|
|
||||||
bpy.utils.register_class(op)
|
|
||||||
|
bpy.utils.register_class(op)
|
||||||
|
@ -15,28 +15,29 @@ class op(bpy.types.Operator):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
|
|
||||||
#Only in UV editor mode
|
# Only in UV editor mode
|
||||||
if bpy.context.area.type != 'IMAGE_EDITOR':
|
if bpy.context.area.type != 'IMAGE_EDITOR':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
export_colors(self, context)
|
export_colors(self, context)
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def export_colors(self, context):
|
def export_colors(self, context):
|
||||||
|
|
||||||
hex_colors = []
|
hex_colors = []
|
||||||
for i in range(bpy.context.scene.texToolsSettings.color_ID_count):
|
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,
|
||||||
hex_colors.append( utilities_color.color_to_hex( color) )
|
"color_ID_color_{}".format(i))
|
||||||
|
hex_colors.append(utilities_color.color_to_hex(color))
|
||||||
|
|
||||||
bpy.context.window_manager.clipboard = ", ".join(hex_colors)
|
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)
|
bpy.utils.register_class(op)
|
||||||
|
@ -7,13 +7,14 @@ from math import pi
|
|||||||
|
|
||||||
from . import utilities_color
|
from . import utilities_color
|
||||||
|
|
||||||
|
|
||||||
class op(bpy.types.Operator):
|
class op(bpy.types.Operator):
|
||||||
bl_idname = "uv.textools_color_select"
|
bl_idname = "uv.textools_color_select"
|
||||||
bl_label = "Assign Color"
|
bl_label = "Assign Color"
|
||||||
bl_description = "Select faces by this color"
|
bl_description = "Select faces by this color"
|
||||||
bl_options = {'REGISTER', 'UNDO'}
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
index : bpy.props.IntProperty(description="Color Index", default=0)
|
index: bpy.props.IntProperty(description="Color Index", default=0)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
@ -30,40 +31,42 @@ class op(bpy.types.Operator):
|
|||||||
if bpy.context.active_object.type != 'MESH':
|
if bpy.context.active_object.type != 'MESH':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#Only in UV editor mode
|
# Only in UV editor mode
|
||||||
if bpy.context.area.type != 'IMAGE_EDITOR':
|
if bpy.context.area.type != 'IMAGE_EDITOR':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
select_color(self, context, self.index)
|
select_color(self, context, self.index)
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def select_color(self, context, index):
|
def select_color(self, context, index):
|
||||||
print("Color select "+str(index) )
|
print("Color select "+str(index))
|
||||||
|
|
||||||
obj = bpy.context.active_object
|
obj = bpy.context.active_object
|
||||||
|
|
||||||
# Check for missing slots, materials,..
|
# Check for missing slots, materials,..
|
||||||
if index >= len(obj.material_slots):
|
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
|
return
|
||||||
|
|
||||||
if not obj.material_slots[index].material:
|
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'},
|
||||||
return
|
"No material found for material slot '{}'".format(index))
|
||||||
|
return
|
||||||
|
|
||||||
if bpy.context.active_object.mode != 'EDIT':
|
if bpy.context.active_object.mode != 'EDIT':
|
||||||
bpy.ops.object.mode_set(mode='EDIT')
|
bpy.ops.object.mode_set(mode='EDIT')
|
||||||
|
|
||||||
# Select faces
|
# 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')
|
bpy.ops.mesh.select_all(action='DESELECT')
|
||||||
for face in bm.faces:
|
for face in bm.faces:
|
||||||
if face.material_index == index:
|
if face.material_index == index:
|
||||||
face.select = True
|
face.select = True
|
||||||
|
|
||||||
bpy.utils.register_class(op)
|
|
||||||
|
bpy.utils.register_class(op)
|
||||||
|
@ -6,71 +6,64 @@ import operator
|
|||||||
|
|
||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from itertools import chain # 'flattens' collection of iterables
|
from itertools import chain # 'flattens' collection of iterables
|
||||||
|
|
||||||
from . import utilities_uv
|
from . import utilities_uv
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class op(bpy.types.Operator):
|
class op(bpy.types.Operator):
|
||||||
bl_idname = "uv.textools_edge_split_bevel"
|
bl_idname = "uv.textools_edge_split_bevel"
|
||||||
bl_label = "Split Bevel"
|
bl_label = "Split Bevel"
|
||||||
bl_description = "..."
|
bl_description = "..."
|
||||||
bl_options = {'REGISTER', 'UNDO'}
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
radius : bpy.props.FloatProperty(
|
radius: bpy.props.FloatProperty(
|
||||||
name = "Space",
|
name="Space",
|
||||||
description = "Space for split bevel",
|
description="Space for split bevel",
|
||||||
default = 0.015,
|
default=0.015,
|
||||||
min = 0,
|
min=0,
|
||||||
max = 0.35
|
max=0.35
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
if not bpy.context.active_object:
|
if not bpy.context.active_object:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#Only in Edit mode
|
# Only in Edit mode
|
||||||
if bpy.context.active_object.mode != 'EDIT':
|
if bpy.context.active_object.mode != 'EDIT':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#Requires UV map
|
# Requires UV map
|
||||||
if not bpy.context.object.data.uv_layers:
|
if not bpy.context.object.data.uv_layers:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if bpy.context.scene.tool_settings.use_uv_select_sync:
|
if bpy.context.scene.tool_settings.use_uv_select_sync:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#Only in UV editor mode
|
# Only in UV editor mode
|
||||||
if bpy.context.area.type != 'IMAGE_EDITOR':
|
if bpy.context.area.type != 'IMAGE_EDITOR':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
main(self, self.radius)
|
main(self, self.radius)
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def main(self, radius):
|
def main(self, radius):
|
||||||
|
|
||||||
#Store selection
|
# Store selection
|
||||||
utilities_uv.selection_store()
|
utilities_uv.selection_store()
|
||||||
|
|
||||||
print("____________\nedge split UV sharp edges {}".format(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()
|
||||||
|
|
||||||
obj = bpy.context.object
|
|
||||||
bm = bmesh.from_edit_mesh(bpy.context.active_object.data);
|
|
||||||
uv_layers = bm.loops.layers.uv.verify();
|
|
||||||
|
|
||||||
islands = utilities_uv.getSelectionIslands()
|
islands = utilities_uv.getSelectionIslands()
|
||||||
|
|
||||||
|
|
||||||
# Collect UV to Vert
|
# Collect UV to Vert
|
||||||
vert_to_uv = utilities_uv.get_vert_to_uv(bm, uv_layers)
|
vert_to_uv = utilities_uv.get_vert_to_uv(bm, uv_layers)
|
||||||
@ -84,19 +77,16 @@ def main(self, radius):
|
|||||||
# print("Hard edge: {} - {}".format(edge.verts[0].index, edge.verts[1].index))
|
# print("Hard edge: {} - {}".format(edge.verts[0].index, edge.verts[1].index))
|
||||||
edges.append(edge)
|
edges.append(edge)
|
||||||
|
|
||||||
# Get vert rails to slide
|
# Get vert rails to slide
|
||||||
vert_rails = get_vert_edge_rails(edges)
|
vert_rails = get_vert_edge_rails(edges)
|
||||||
|
|
||||||
# Get left and right faces
|
# Get left and right faces
|
||||||
edge_face_pairs = get_edge_face_pairs(edges)
|
edge_face_pairs = get_edge_face_pairs(edges)
|
||||||
|
|
||||||
|
|
||||||
print("Vert rails: {}x".format(len(vert_rails)))
|
print("Vert rails: {}x".format(len(vert_rails)))
|
||||||
# for vert in vert_rails:
|
# for vert in vert_rails:
|
||||||
# print(".. v.idx {} = {}x".format(vert.index, len(vert_rails[vert]) ))
|
# print(".. v.idx {} = {}x".format(vert.index, len(vert_rails[vert]) ))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
vert_processed = []
|
vert_processed = []
|
||||||
vert_uv_pos = []
|
vert_uv_pos = []
|
||||||
|
|
||||||
@ -104,77 +94,79 @@ def main(self, radius):
|
|||||||
if len(edge_face_pairs[edge]) == 2:
|
if len(edge_face_pairs[edge]) == 2:
|
||||||
v0 = edge.verts[0]
|
v0 = edge.verts[0]
|
||||||
v1 = edge.verts[1]
|
v1 = edge.verts[1]
|
||||||
|
|
||||||
f0 = edge_face_pairs[edge][0]
|
f0 = edge_face_pairs[edge][0]
|
||||||
f1 = edge_face_pairs[edge][1]
|
f1 = edge_face_pairs[edge][1]
|
||||||
|
|
||||||
# v0
|
# v0
|
||||||
if v0 not in vert_processed:
|
if v0 not in vert_processed:
|
||||||
vert_processed.append(v0)
|
vert_processed.append(v0)
|
||||||
faces, origin, delta = slide_uvs(v0, edge, f0, edges, vert_rails, vert_to_uv)
|
faces, origin, delta = slide_uvs(
|
||||||
vert_uv_pos.append( {"v":v0, "f":f0, "origin":origin, "delta":delta, "faces":faces} )
|
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)
|
faces, origin, delta = slide_uvs(
|
||||||
vert_uv_pos.append( {"v":v0, "f":f1, "origin":origin, "delta":delta, "faces":faces} )
|
v0, edge, f1, edges, vert_rails, vert_to_uv)
|
||||||
|
vert_uv_pos.append(
|
||||||
|
{"v": v0, "f": f1, "origin": origin, "delta": delta, "faces": faces})
|
||||||
|
|
||||||
# V1
|
# V1
|
||||||
if v1 not in vert_processed:
|
if v1 not in vert_processed:
|
||||||
vert_processed.append(v1)
|
vert_processed.append(v1)
|
||||||
faces, origin, delta = slide_uvs(v1, edge, f0, edges, vert_rails, vert_to_uv)
|
faces, origin, delta = slide_uvs(
|
||||||
vert_uv_pos.append( {"v":v1, "f":f0, "origin":origin, "delta":delta, "faces":faces} )
|
v1, edge, f0, edges, vert_rails, vert_to_uv)
|
||||||
|
vert_uv_pos.append(
|
||||||
faces, origin, delta = slide_uvs(v1, edge, f1, edges, vert_rails, vert_to_uv)
|
{"v": v1, "f": f0, "origin": origin, "delta": delta, "faces": faces})
|
||||||
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:
|
for item in vert_uv_pos:
|
||||||
v = item["v"]
|
v = item["v"]
|
||||||
|
|
||||||
|
|
||||||
for face in item["faces"]:
|
for face in item["faces"]:
|
||||||
if v in face.verts:
|
if v in face.verts:
|
||||||
for loop in face.loops:
|
for loop in face.loops:
|
||||||
if loop.vert == v:
|
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 f in faces:
|
||||||
# for loop in f.loops:
|
# for loop in f.loops:
|
||||||
# if loop.vert == vert:
|
# if loop.vert == vert:
|
||||||
# loop[uv_layers].uv= vert_to_uv[vert][0].uv + item["delta"] * radius/2
|
# loop[uv_layers].uv= vert_to_uv[vert][0].uv + item["delta"] * radius/2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# for loop in face.loops:
|
# for loop in face.loops:
|
||||||
# if loop.vert == vert:
|
# if loop.vert == vert:
|
||||||
# loop[uv_layers].uv+= avg_uv_delta
|
# loop[uv_layers].uv+= avg_uv_delta
|
||||||
|
|
||||||
|
# Restore selection
|
||||||
|
|
||||||
#Restore selection
|
|
||||||
utilities_uv.selection_restore()
|
utilities_uv.selection_restore()
|
||||||
|
|
||||||
|
|
||||||
def slide_uvs(vert, edge, face, edges, vert_rails, vert_to_uv):
|
def slide_uvs(vert, edge, face, edges, vert_rails, vert_to_uv):
|
||||||
|
|
||||||
def IS_DEBUG():
|
def IS_DEBUG():
|
||||||
return vert.index == 64 and edge.verts[0].index == 64 and edge.verts[1].index == 63
|
return vert.index == 64 and edge.verts[0].index == 64 and edge.verts[1].index == 63
|
||||||
|
|
||||||
|
|
||||||
A = edge.verts[0]
|
A = edge.verts[0]
|
||||||
B = edge.verts[1]
|
B = edge.verts[1]
|
||||||
A_links, B_links = get_edge_prev_next(edge, edges)
|
A_links, B_links = get_edge_prev_next(edge, edges)
|
||||||
|
|
||||||
verts_edges = {edge.verts[0], edge.verts[1]}
|
verts_edges = {edge.verts[0], edge.verts[1]}
|
||||||
for v in A_links:
|
for v in A_links:
|
||||||
verts_edges.add( v )
|
verts_edges.add(v)
|
||||||
for v in B_links:
|
for v in B_links:
|
||||||
verts_edges.add( v )
|
verts_edges.add(v)
|
||||||
|
|
||||||
if IS_DEBUG():
|
if IS_DEBUG():
|
||||||
print("\r")
|
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
|
# Collect faces of this side
|
||||||
|
|
||||||
@ -207,12 +199,12 @@ def slide_uvs(vert, edge, face, edges, vert_rails, vert_to_uv):
|
|||||||
if IS_DEBUG():
|
if IS_DEBUG():
|
||||||
print(" Faces {}x = {}".format(len(faces), [f.index for f in faces]))
|
print(" Faces {}x = {}".format(len(faces), [f.index for f in faces]))
|
||||||
|
|
||||||
|
|
||||||
# Get all face edges that could be valid rails
|
# 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
|
# The verts influencing the offset
|
||||||
verts = [A,B]
|
verts = [A, B]
|
||||||
if vert == A:
|
if vert == A:
|
||||||
verts.extend(B_links)
|
verts.extend(B_links)
|
||||||
elif vert == B:
|
elif vert == 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(" Verts: {}x = {}".format(len(verts), [v.index for v in verts]))
|
||||||
print(" Rails:")
|
print(" Rails:")
|
||||||
|
|
||||||
|
delta = Vector((0, 0))
|
||||||
delta = Vector((0,0))
|
|
||||||
count = 0.0
|
count = 0.0
|
||||||
for v in verts:
|
for v in verts:
|
||||||
rails = [e for e in vert_rails[v] if e in face_edges]
|
rails = [e for e in vert_rails[v] if e in face_edges]
|
||||||
|
|
||||||
if IS_DEBUG():
|
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:
|
for e in rails:
|
||||||
# determine order
|
# determine order
|
||||||
@ -248,7 +239,7 @@ def slide_uvs(vert, edge, face, edges, vert_rails, vert_to_uv):
|
|||||||
delta += (uv1-uv0).normalized()
|
delta += (uv1-uv0).normalized()
|
||||||
count += 1.0
|
count += 1.0
|
||||||
|
|
||||||
delta/=count
|
delta /= count
|
||||||
|
|
||||||
if IS_DEBUG():
|
if IS_DEBUG():
|
||||||
print("\r")
|
print("\r")
|
||||||
@ -261,7 +252,6 @@ def slide_uvs(vert, edge, face, edges, vert_rails, vert_to_uv):
|
|||||||
# loop[uv_layers].uv+= avg_uv_delta
|
# loop[uv_layers].uv+= avg_uv_delta
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
def slide_face_uvs(uv_layers, edge, vert, face, radius, vert_to_uv):
|
def slide_face_uvs(uv_layers, edge, vert, face, radius, vert_to_uv):
|
||||||
avg_target = Vector((0,0))
|
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)
|
# Get all rails (max 3x: current, before and after)
|
||||||
rails = [e for v in verts_edges for e in vert_rails[v]]
|
rails = [e for v in verts_edges for e in vert_rails[v]]
|
||||||
@ -310,20 +294,20 @@ def slide_face_uvs(uv_layers, edge, vert, face, radius, vert_to_uv):
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_edge_prev_next(edge, edges):
|
def get_edge_prev_next(edge, edges):
|
||||||
A = edge.verts[0]
|
A = edge.verts[0]
|
||||||
B = edge.verts[1]
|
B = edge.verts[1]
|
||||||
|
|
||||||
# print(" get_edge_prev_next {}x edges".format(len(edges)))
|
# print(" get_edge_prev_next {}x edges".format(len(edges)))
|
||||||
# v0_extends = []
|
# v0_extends = []
|
||||||
# v0_extends = [v for e in edges for v in e.verts if v in edge.verts and e != edge and v != v0]
|
# v0_extends = [v for e in edges for v in e.verts if v in edge.verts and e != edge and v != v0]
|
||||||
# v1_extends = [v for e in edges for v in e.verts if v in edge.verts and e != edge and v != v1]
|
# 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]
|
# 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]
|
A_extends = [v2 for v1 in edge.verts for e in v1.link_edges for v2 in e.verts if e !=
|
||||||
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]
|
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
|
return A_extends, B_extends
|
||||||
|
|
||||||
@ -342,7 +326,6 @@ def get_edge_face_pairs(edges):
|
|||||||
return edge_faces
|
return edge_faces
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_vert_edge_rails(edges):
|
def get_vert_edge_rails(edges):
|
||||||
|
|
||||||
vert_rails = {}
|
vert_rails = {}
|
||||||
@ -359,9 +342,9 @@ def get_vert_edge_rails(edges):
|
|||||||
for e in face.edges:
|
for e in face.edges:
|
||||||
if e not in edges and len(e.link_faces) > 0:
|
if e not in edges and len(e.link_faces) > 0:
|
||||||
if v0 not in vert_rails:
|
if v0 not in vert_rails:
|
||||||
vert_rails[ v0 ] = []
|
vert_rails[v0] = []
|
||||||
if v1 not in vert_rails:
|
if v1 not in vert_rails:
|
||||||
vert_rails[ v1 ] = []
|
vert_rails[v1] = []
|
||||||
|
|
||||||
if v0 in e.verts and e not in vert_rails[v0]:
|
if v0 in e.verts and e not in vert_rails[v0]:
|
||||||
vert_rails[v0].append(e)
|
vert_rails[v0].append(e)
|
||||||
|
@ -8,15 +8,16 @@ from math import pi
|
|||||||
|
|
||||||
from . import utilities_uv
|
from . import utilities_uv
|
||||||
|
|
||||||
|
|
||||||
class op(bpy.types.Operator):
|
class op(bpy.types.Operator):
|
||||||
bl_idname = "uv.textools_island_align_edge"
|
bl_idname = "uv.textools_island_align_edge"
|
||||||
bl_label = "Align Island by Edge"
|
bl_label = "Align Island by Edge"
|
||||||
bl_description = "Align the island by selected edge"
|
bl_description = "Align the island by selected edge"
|
||||||
bl_options = {'REGISTER', 'UNDO'}
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
#Only in UV editor mode
|
# Only in UV editor mode
|
||||||
if bpy.context.area.type != 'IMAGE_EDITOR':
|
if bpy.context.area.type != 'IMAGE_EDITOR':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -26,51 +27,49 @@ class op(bpy.types.Operator):
|
|||||||
if bpy.context.active_object.type != 'MESH':
|
if bpy.context.active_object.type != 'MESH':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#Only in Edit mode
|
# Only in Edit mode
|
||||||
if bpy.context.active_object.mode != 'EDIT':
|
if bpy.context.active_object.mode != 'EDIT':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if bpy.context.scene.tool_settings.use_uv_select_sync:
|
if bpy.context.scene.tool_settings.use_uv_select_sync:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#Requires UV map
|
# Requires UV map
|
||||||
if not bpy.context.object.data.uv_layers:
|
if not bpy.context.object.data.uv_layers:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Requires UV Edge select mode
|
# Requires UV Edge select mode
|
||||||
if bpy.context.scene.tool_settings.uv_select_mode != 'EDGE':
|
if bpy.context.scene.tool_settings.uv_select_mode != 'EDGE':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
#Store selection
|
# Store selection
|
||||||
utilities_uv.selection_store()
|
utilities_uv.selection_store()
|
||||||
|
|
||||||
main(context)
|
main(context)
|
||||||
|
|
||||||
#Restore selection
|
# Restore selection
|
||||||
utilities_uv.selection_restore()
|
utilities_uv.selection_restore()
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def main(context):
|
def main(context):
|
||||||
print("Executing operator_island_align_edge")
|
print("Executing operator_island_align_edge")
|
||||||
|
|
||||||
bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
|
bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
|
||||||
uv_layers = bm.loops.layers.uv.verify()
|
uv_layers = bm.loops.layers.uv.verify()
|
||||||
|
|
||||||
faces_selected = [];
|
faces_selected = []
|
||||||
for face in bm.faces:
|
for face in bm.faces:
|
||||||
if face.select:
|
if face.select:
|
||||||
for loop in face.loops:
|
for loop in face.loops:
|
||||||
if loop[uv_layers].select:
|
if loop[uv_layers].select:
|
||||||
faces_selected.append(face)
|
faces_selected.append(face)
|
||||||
break
|
break
|
||||||
|
|
||||||
print("faces_selected: "+str(len(faces_selected)))
|
print("faces_selected: "+str(len(faces_selected)))
|
||||||
|
|
||||||
# Collect 2 uv verts for each island
|
# Collect 2 uv verts for each island
|
||||||
@ -91,11 +90,11 @@ def main(context):
|
|||||||
if face in faces_unparsed:
|
if face in faces_unparsed:
|
||||||
|
|
||||||
bpy.ops.uv.select_all(action='DESELECT')
|
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
|
bpy.ops.uv.select_linked() # Extend selection
|
||||||
|
|
||||||
#Collect faces
|
# Collect faces
|
||||||
faces_island = [face];
|
faces_island = [face]
|
||||||
for f in faces_unparsed:
|
for f in faces_unparsed:
|
||||||
if f != face and f.select and f.loops[0][uv_layers].select:
|
if f != face and f.select and f.loops[0][uv_layers].select:
|
||||||
print("append "+str(f.index))
|
print("append "+str(f.index))
|
||||||
@ -103,14 +102,15 @@ def main(context):
|
|||||||
for f in faces_island:
|
for f in faces_island:
|
||||||
faces_unparsed.remove(f)
|
faces_unparsed.remove(f)
|
||||||
|
|
||||||
#Assign Faces to island
|
# Assign Faces to island
|
||||||
faces_islands[face] = faces_island
|
faces_islands[face] = faces_island
|
||||||
|
|
||||||
print("Sets: {}x".format(len(faces_islands)))
|
print("Sets: {}x".format(len(faces_islands)))
|
||||||
|
|
||||||
# Align each island to its edges
|
# Align each island to its edges
|
||||||
for face in faces_islands:
|
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):
|
def align_island(uv_vert0, uv_vert1, faces):
|
||||||
@ -126,7 +126,7 @@ def align_island(uv_vert0, uv_vert1, faces):
|
|||||||
loop[uv_layers].select = True
|
loop[uv_layers].select = True
|
||||||
|
|
||||||
diff = uv_vert1 - uv_vert0
|
diff = uv_vert1 - uv_vert0
|
||||||
angle = math.atan2(diff.y, diff.x)%(math.pi/2)
|
angle = math.atan2(diff.y, diff.x) % (math.pi/2)
|
||||||
|
|
||||||
bpy.ops.uv.select_linked()
|
bpy.ops.uv.select_linked()
|
||||||
|
|
||||||
@ -136,7 +136,8 @@ def align_island(uv_vert0, uv_vert1, faces):
|
|||||||
if angle >= (math.pi/4):
|
if angle >= (math.pi/4):
|
||||||
angle = angle - (math.pi/2)
|
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)
|
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_description = "Rotates UV islands to minimal bounds and sorts them horizontal or vertical"
|
||||||
bl_options = {'REGISTER', 'UNDO'}
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
|
is_vertical: bpy.props.BoolProperty(
|
||||||
is_vertical : bpy.props.BoolProperty(description="Vertical or Horizontal orientation", default=True)
|
description="Vertical or Horizontal orientation", default=True)
|
||||||
padding : bpy.props.FloatProperty(description="Padding between UV islands", default=0.05)
|
padding: bpy.props.FloatProperty(
|
||||||
|
description="Padding between UV islands", default=0.05)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
@ -31,25 +32,25 @@ class op(bpy.types.Operator):
|
|||||||
if bpy.context.active_object.type != 'MESH':
|
if bpy.context.active_object.type != 'MESH':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#Only in Edit mode
|
# Only in Edit mode
|
||||||
if bpy.context.active_object.mode != 'EDIT':
|
if bpy.context.active_object.mode != 'EDIT':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#Only in UV editor mode
|
# Only in UV editor mode
|
||||||
if bpy.context.area.type != 'IMAGE_EDITOR':
|
if bpy.context.area.type != 'IMAGE_EDITOR':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#Requires UV map
|
|
||||||
if not bpy.context.object.data.uv_layers:
|
|
||||||
return False #self.report({'WARNING'}, "Object must have more than one UV map")
|
|
||||||
|
|
||||||
#Not in Synced mode
|
# Requires UV map
|
||||||
|
if not bpy.context.object.data.uv_layers:
|
||||||
|
# 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:
|
if bpy.context.scene.tool_settings.use_uv_select_sync:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
main(context, self.is_vertical, self.padding)
|
main(context, self.is_vertical, self.padding)
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
@ -57,49 +58,48 @@ class op(bpy.types.Operator):
|
|||||||
|
|
||||||
def main(context, isVertical, padding):
|
def main(context, isVertical, padding):
|
||||||
print("Executing IslandsAlignSort main {}".format(padding))
|
print("Executing IslandsAlignSort main {}".format(padding))
|
||||||
|
|
||||||
#Store selection
|
# Store selection
|
||||||
utilities_uv.selection_store()
|
utilities_uv.selection_store()
|
||||||
|
|
||||||
if bpy.context.tool_settings.transform_pivot_point != 'CURSOR':
|
if bpy.context.tool_settings.transform_pivot_point != 'CURSOR':
|
||||||
bpy.context.tool_settings.transform_pivot_point = 'CURSOR'
|
bpy.context.tool_settings.transform_pivot_point = 'CURSOR'
|
||||||
|
|
||||||
#Only in Face or Island mode
|
# Only in Face or Island mode
|
||||||
if bpy.context.scene.tool_settings.uv_select_mode is not 'FACE' or 'ISLAND':
|
if bpy.context.scene.tool_settings.uv_select_mode is not 'FACE' or 'ISLAND':
|
||||||
bpy.context.scene.tool_settings.uv_select_mode = 'FACE'
|
bpy.context.scene.tool_settings.uv_select_mode = 'FACE'
|
||||||
|
|
||||||
bm = bmesh.from_edit_mesh(bpy.context.active_object.data);
|
bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
|
||||||
uv_layers = bm.loops.layers.uv.verify();
|
uv_layers = bm.loops.layers.uv.verify()
|
||||||
|
|
||||||
|
|
||||||
boundsAll = utilities_uv.getSelectionBBox()
|
boundsAll = utilities_uv.getSelectionBBox()
|
||||||
|
|
||||||
|
|
||||||
islands = utilities_uv.getSelectionIslands()
|
islands = utilities_uv.getSelectionIslands()
|
||||||
allSizes = {} #https://stackoverflow.com/questions/613183/sort-a-python-dictionary-by-value
|
allSizes = {} # https://stackoverflow.com/questions/613183/sort-a-python-dictionary-by-value
|
||||||
allBounds = {}
|
allBounds = {}
|
||||||
|
|
||||||
print("Islands: "+str(len(islands))+"x")
|
print("Islands: "+str(len(islands))+"x")
|
||||||
|
|
||||||
bpy.context.window_manager.progress_begin(0, len(islands))
|
bpy.context.window_manager.progress_begin(0, len(islands))
|
||||||
|
|
||||||
#Rotate to minimal bounds
|
# Rotate to minimal bounds
|
||||||
for i in range(0, len(islands)):
|
for i in range(0, len(islands)):
|
||||||
alignIslandMinimalBounds(uv_layers, islands[i])
|
alignIslandMinimalBounds(uv_layers, islands[i])
|
||||||
|
|
||||||
# Collect BBox sizes
|
# Collect BBox sizes
|
||||||
bounds = utilities_uv.getSelectionBBox()
|
bounds = utilities_uv.getSelectionBBox()
|
||||||
allSizes[i] = max(bounds['width'], bounds['height']) + i*0.000001;#Make each size unique
|
allSizes[i] = max(bounds['width'], bounds['height']) + \
|
||||||
allBounds[i] = bounds;
|
i*0.000001 # Make each size unique
|
||||||
|
allBounds[i] = bounds
|
||||||
print("Rotate compact: "+str(allSizes[i]))
|
print("Rotate compact: "+str(allSizes[i]))
|
||||||
|
|
||||||
bpy.context.window_manager.progress_update(i)
|
bpy.context.window_manager.progress_update(i)
|
||||||
|
|
||||||
bpy.context.window_manager.progress_end()
|
bpy.context.window_manager.progress_end()
|
||||||
|
|
||||||
|
# Position by sorted size in row
|
||||||
#Position by sorted size in row
|
# Sort by values, store tuples
|
||||||
sortedSizes = sorted(allSizes.items(), key=operator.itemgetter(1))#Sort by values, store tuples
|
sortedSizes = sorted(allSizes.items(), key=operator.itemgetter(1))
|
||||||
sortedSizes.reverse()
|
sortedSizes.reverse()
|
||||||
offset = 0.0
|
offset = 0.0
|
||||||
for sortedSize in sortedSizes:
|
for sortedSize in sortedSizes:
|
||||||
@ -107,23 +107,24 @@ def main(context, isVertical, padding):
|
|||||||
island = islands[index]
|
island = islands[index]
|
||||||
bounds = allBounds[index]
|
bounds = allBounds[index]
|
||||||
|
|
||||||
#Select Island
|
# Select Island
|
||||||
bpy.ops.uv.select_all(action='DESELECT')
|
bpy.ops.uv.select_all(action='DESELECT')
|
||||||
utilities_uv.set_selected_faces(island)
|
utilities_uv.set_selected_faces(island)
|
||||||
|
|
||||||
#Offset Island
|
# Offset Island
|
||||||
if(isVertical):
|
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))
|
bpy.ops.transform.translate(value=(delta.x, delta.y-offset, 0))
|
||||||
offset += bounds['height']+padding
|
offset += bounds['height']+padding
|
||||||
else:
|
else:
|
||||||
print("Horizontal")
|
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))
|
bpy.ops.transform.translate(value=(delta.x+offset, delta.y, 0))
|
||||||
offset += bounds['width']+padding
|
offset += bounds['width']+padding
|
||||||
|
|
||||||
|
# Restore selection
|
||||||
#Restore selection
|
|
||||||
utilities_uv.selection_restore()
|
utilities_uv.selection_restore()
|
||||||
|
|
||||||
|
|
||||||
@ -133,13 +134,14 @@ def alignIslandMinimalBounds(uv_layers, faces):
|
|||||||
utilities_uv.set_selected_faces(faces)
|
utilities_uv.set_selected_faces(faces)
|
||||||
|
|
||||||
steps = 8
|
steps = 8
|
||||||
angle = 45; # Starting Angle, half each step
|
angle = 45 # Starting Angle, half each step
|
||||||
|
|
||||||
bboxPrevious = utilities_uv.getSelectionBBox()
|
bboxPrevious = utilities_uv.getSelectionBBox()
|
||||||
|
|
||||||
for i in range(0, steps):
|
for i in range(0, steps):
|
||||||
# Rotate right
|
# 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()
|
bbox = utilities_uv.getSelectionBBox()
|
||||||
|
|
||||||
if i == 0:
|
if i == 0:
|
||||||
@ -147,25 +149,28 @@ def alignIslandMinimalBounds(uv_layers, faces):
|
|||||||
sizeB = bbox['width'] * bbox['height']
|
sizeB = bbox['width'] * bbox['height']
|
||||||
if abs(bbox['width'] - bbox['height']) <= 0.0001 and sizeA < sizeB:
|
if abs(bbox['width'] - bbox['height']) <= 0.0001 and sizeA < sizeB:
|
||||||
# print("Already squared")
|
# print("Already squared")
|
||||||
bpy.ops.transform.rotate(value=(-angle * math.pi / 180), orient_axis='Z')
|
bpy.ops.transform.rotate(
|
||||||
break;
|
value=(-angle * math.pi / 180), orient_axis='Z')
|
||||||
|
break
|
||||||
|
|
||||||
if bbox['minLength'] < bboxPrevious['minLength']:
|
if bbox['minLength'] < bboxPrevious['minLength']:
|
||||||
bboxPrevious = bbox; # Success
|
bboxPrevious = bbox # Success
|
||||||
else:
|
else:
|
||||||
# Rotate Left
|
# 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()
|
bbox = utilities_uv.getSelectionBBox()
|
||||||
if bbox['minLength'] < bboxPrevious['minLength']:
|
if bbox['minLength'] < bboxPrevious['minLength']:
|
||||||
bboxPrevious = bbox; # Success
|
bboxPrevious = bbox # Success
|
||||||
else:
|
else:
|
||||||
# Restore angle of this iteration
|
# 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
|
angle = angle / 2
|
||||||
|
|
||||||
if bboxPrevious['width'] < bboxPrevious['height']:
|
if bboxPrevious['width'] < bboxPrevious['height']:
|
||||||
bpy.ops.transform.rotate(value=(90 * math.pi / 180), orient_axis='Z')
|
bpy.ops.transform.rotate(value=(90 * math.pi / 180), orient_axis='Z')
|
||||||
|
|
||||||
bpy.utils.register_class(op)
|
|
||||||
|
bpy.utils.register_class(op)
|
||||||
|
@ -6,13 +6,11 @@ import operator
|
|||||||
|
|
||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from itertools import chain # 'flattens' collection of iterables
|
from itertools import chain # 'flattens' collection of iterables
|
||||||
|
|
||||||
from . import utilities_uv
|
from . import utilities_uv
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class op(bpy.types.Operator):
|
class op(bpy.types.Operator):
|
||||||
bl_idname = "uv.textools_island_align_world"
|
bl_idname = "uv.textools_island_align_world"
|
||||||
bl_label = "Align World"
|
bl_label = "Align World"
|
||||||
@ -34,57 +32,53 @@ class op(bpy.types.Operator):
|
|||||||
if not bpy.context.active_object:
|
if not bpy.context.active_object:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#Only in Edit mode
|
# Only in Edit mode
|
||||||
if bpy.context.active_object.mode != 'EDIT':
|
if bpy.context.active_object.mode != 'EDIT':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#Requires UV map
|
# Requires UV map
|
||||||
if not bpy.context.object.data.uv_layers:
|
if not bpy.context.object.data.uv_layers:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if bpy.context.scene.tool_settings.use_uv_select_sync:
|
if bpy.context.scene.tool_settings.use_uv_select_sync:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#Only in UV editor mode
|
# Only in UV editor mode
|
||||||
if bpy.context.area.type != 'IMAGE_EDITOR':
|
if bpy.context.area.type != 'IMAGE_EDITOR':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
main(self)
|
main(self)
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def main(context):
|
def main(context):
|
||||||
print("\n________________________\nis_global")
|
print("\n________________________\nis_global")
|
||||||
|
|
||||||
#Store selection
|
# Store selection
|
||||||
utilities_uv.selection_store()
|
utilities_uv.selection_store()
|
||||||
|
|
||||||
bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
|
bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
|
||||||
uv_layers = bm.loops.layers.uv.verify()
|
uv_layers = bm.loops.layers.uv.verify()
|
||||||
|
|
||||||
#Only in Face or Island mode
|
# Only in Face or Island mode
|
||||||
if bpy.context.scene.tool_settings.uv_select_mode is not 'FACE' or 'ISLAND':
|
if bpy.context.scene.tool_settings.uv_select_mode is not 'FACE' or 'ISLAND':
|
||||||
bpy.context.scene.tool_settings.uv_select_mode = 'FACE'
|
bpy.context.scene.tool_settings.uv_select_mode = 'FACE'
|
||||||
|
|
||||||
obj = bpy.context.object
|
obj = bpy.context.object
|
||||||
bm = bmesh.from_edit_mesh(bpy.context.active_object.data);
|
bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
|
||||||
uv_layers = bm.loops.layers.uv.verify();
|
uv_layers = bm.loops.layers.uv.verify()
|
||||||
|
|
||||||
islands = utilities_uv.getSelectionIslands()
|
|
||||||
|
|
||||||
|
islands = utilities_uv.getSelectionIslands()
|
||||||
|
|
||||||
for faces in islands:
|
for faces in islands:
|
||||||
# Get average viewport normal of UV island
|
# Get average viewport normal of UV island
|
||||||
avg_normal = Vector((0,0,0))
|
avg_normal = Vector((0, 0, 0))
|
||||||
for face in faces:
|
for face in faces:
|
||||||
avg_normal+=face.normal
|
avg_normal += face.normal
|
||||||
avg_normal/=len(faces)
|
avg_normal /= len(faces)
|
||||||
|
|
||||||
# avg_normal = (obj.matrix_world*avg_normal).normalized()
|
# avg_normal = (obj.matrix_world*avg_normal).normalized()
|
||||||
|
|
||||||
@ -93,44 +87,43 @@ def main(context):
|
|||||||
y = 1
|
y = 1
|
||||||
z = 2
|
z = 2
|
||||||
max_size = max(abs(avg_normal.x), abs(avg_normal.y), abs(avg_normal.z))
|
max_size = max(abs(avg_normal.x), abs(avg_normal.y), abs(avg_normal.z))
|
||||||
|
|
||||||
# Use multiple steps
|
# Use multiple steps
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
if(abs(avg_normal.x) == max_size):
|
if(abs(avg_normal.x) == max_size):
|
||||||
print("x normal")
|
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):
|
elif(abs(avg_normal.y) == max_size):
|
||||||
print("y normal")
|
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):
|
elif(abs(avg_normal.z) == max_size):
|
||||||
print("z normal")
|
print("z normal")
|
||||||
align_island(obj, bm, uv_layers, faces, x, y, False, avg_normal.z < 0)
|
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))
|
print("align island: faces {}x n:{}, max:{}".format(
|
||||||
|
len(faces), avg_normal, max_size))
|
||||||
|
|
||||||
|
# Restore selection
|
||||||
|
|
||||||
#Restore selection
|
|
||||||
utilities_uv.selection_restore()
|
utilities_uv.selection_restore()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def align_island(obj, bm, uv_layers, faces, x=0, y=1, flip_x=False, flip_y=False):
|
def align_island(obj, bm, uv_layers, faces, x=0, y=1, flip_x=False, flip_y=False):
|
||||||
|
|
||||||
# Find lowest and highest verts
|
# Find lowest and highest verts
|
||||||
minmax_val = [0,0]
|
minmax_val = [0, 0]
|
||||||
minmax_vert = [None, None]
|
minmax_vert = [None, None]
|
||||||
|
|
||||||
axis_names = ['x', 'y', 'z']
|
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] ))
|
|
||||||
|
|
||||||
|
|
||||||
|
# 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
|
# Collect UV to Vert
|
||||||
vert_to_uv = utilities_uv.get_vert_to_uv(bm, uv_layers)
|
vert_to_uv = utilities_uv.get_vert_to_uv(bm, uv_layers)
|
||||||
@ -142,33 +135,31 @@ def align_island(obj, bm, uv_layers, faces, x=0, y=1, flip_x=False, flip_y=False
|
|||||||
for edge in face.edges:
|
for edge in face.edges:
|
||||||
if edge not in processed_edges:
|
if edge not in processed_edges:
|
||||||
processed_edges.append(edge)
|
processed_edges.append(edge)
|
||||||
delta = edge.verts[0].co -edge.verts[1].co
|
delta = edge.verts[0].co - edge.verts[1].co
|
||||||
max_side = max(abs(delta.x), abs(delta.y), abs(delta.z))
|
max_side = max(abs(delta.x), abs(delta.y), abs(delta.z))
|
||||||
|
|
||||||
# Check edges dominant in active axis
|
# Check edges dominant in active axis
|
||||||
if( abs(delta[x]) == max_side or abs(delta[y]) == max_side):
|
if(abs(delta[x]) == max_side or abs(delta[y]) == max_side):
|
||||||
# if( abs(delta[y]) == max_side):
|
# if( abs(delta[y]) == max_side):
|
||||||
edges.append(edge)
|
edges.append(edge)
|
||||||
|
|
||||||
print("Edges {}x".format(len(edges)))
|
print("Edges {}x".format(len(edges)))
|
||||||
|
|
||||||
avg_angle = 0
|
avg_angle = 0
|
||||||
for edge in edges:
|
for edge in edges:
|
||||||
uv0 = vert_to_uv[ edge.verts[0] ][0]
|
uv0 = vert_to_uv[edge.verts[0]][0]
|
||||||
uv1 = vert_to_uv[ edge.verts[1] ][0]
|
uv1 = vert_to_uv[edge.verts[1]][0]
|
||||||
delta_verts = Vector((
|
delta_verts = Vector((
|
||||||
edge.verts[1].co[x] - edge.verts[0].co[x],
|
edge.verts[1].co[x] - edge.verts[0].co[x],
|
||||||
edge.verts[1].co[y] - edge.verts[0].co[y]
|
edge.verts[1].co[y] - edge.verts[0].co[y]
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
||||||
if flip_x:
|
if flip_x:
|
||||||
delta_verts.x = -edge.verts[1].co[x] + edge.verts[0].co[x]
|
delta_verts.x = -edge.verts[1].co[x] + edge.verts[0].co[x]
|
||||||
if flip_y:
|
if flip_y:
|
||||||
delta_verts.y = -edge.verts[1].co[y] + edge.verts[0].co[y]
|
delta_verts.y = -edge.verts[1].co[y] + edge.verts[0].co[y]
|
||||||
|
|
||||||
# delta_verts.y = edge.verts[0].co[y] - edge.verts[1].co[y]
|
# delta_verts.y = edge.verts[0].co[y] - edge.verts[1].co[y]
|
||||||
|
|
||||||
|
|
||||||
delta_uvs = Vector((
|
delta_uvs = Vector((
|
||||||
uv1.uv.x - uv0.uv.x,
|
uv1.uv.x - uv0.uv.x,
|
||||||
@ -177,31 +168,24 @@ 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
|
a0 = math.atan2(delta_verts.y, delta_verts.x) - math.pi/2
|
||||||
a1 = math.atan2(delta_uvs.y, delta_uvs.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))
|
||||||
|
|
||||||
|
|
||||||
a_delta = math.atan2(math.sin(a0-a1), math.cos(a0-a1))
|
|
||||||
# edge.verts[0].index, edge.verts[1].index
|
# 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))
|
# 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 += a_delta
|
||||||
|
|
||||||
|
avg_angle /= len(edges) # - math.pi/2
|
||||||
avg_angle/=len(edges) # - math.pi/2
|
|
||||||
print("Turn {:.1f}".format(avg_angle * 180/math.pi))
|
print("Turn {:.1f}".format(avg_angle * 180/math.pi))
|
||||||
|
|
||||||
bpy.ops.uv.select_all(action='DESELECT')
|
bpy.ops.uv.select_all(action='DESELECT')
|
||||||
for face in faces:
|
for face in faces:
|
||||||
for loop in face.loops:
|
for loop in face.loops:
|
||||||
loop[uv_layers].select = True
|
loop[uv_layers].select = True
|
||||||
|
|
||||||
|
|
||||||
bpy.context.tool_settings.transform_pivot_point = 'MEDIAN_POINT'
|
bpy.context.tool_settings.transform_pivot_point = 'MEDIAN_POINT'
|
||||||
bpy.ops.transform.rotate(value=avg_angle, orient_axis='Z')
|
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)
|
# 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 = []
|
# processed = []
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
bpy.ops.uv.select_all(action='DESELECT')
|
bpy.ops.uv.select_all(action='DESELECT')
|
||||||
@ -288,4 +272,5 @@ def align_island(obj, bm, uv_layers, faces, x=0, y=1, flip_x=False, flip_y=False
|
|||||||
# return
|
# return
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
bpy.utils.register_class(op)
|
bpy.utils.register_class(op)
|
||||||
|
@ -8,12 +8,13 @@ from math import pi
|
|||||||
|
|
||||||
from . import utilities_uv
|
from . import utilities_uv
|
||||||
|
|
||||||
|
|
||||||
class op(bpy.types.Operator):
|
class op(bpy.types.Operator):
|
||||||
bl_idname = "uv.textools_island_straighten_edge_loops"
|
bl_idname = "uv.textools_island_straighten_edge_loops"
|
||||||
bl_label = "Straight edge loops"
|
bl_label = "Straight edge loops"
|
||||||
bl_description = "Straighten edge loops of UV Island and relax rest"
|
bl_description = "Straighten edge loops of UV Island and relax rest"
|
||||||
bl_options = {'REGISTER', 'UNDO'}
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
|
|
||||||
@ -23,54 +24,47 @@ class op(bpy.types.Operator):
|
|||||||
if bpy.context.active_object.type != 'MESH':
|
if bpy.context.active_object.type != 'MESH':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#Only in Edit mode
|
# Only in Edit mode
|
||||||
if bpy.context.active_object.mode != 'EDIT':
|
if bpy.context.active_object.mode != 'EDIT':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#Only in UV editor mode
|
# Only in UV editor mode
|
||||||
if bpy.context.area.type != 'IMAGE_EDITOR':
|
if bpy.context.area.type != 'IMAGE_EDITOR':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#Requires UV map
|
# Requires UV map
|
||||||
if not bpy.context.object.data.uv_layers:
|
if not bpy.context.object.data.uv_layers:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if bpy.context.scene.tool_settings.uv_select_mode != 'EDGE':
|
if bpy.context.scene.tool_settings.uv_select_mode != 'EDGE':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
|
|
||||||
main(context)
|
main(context)
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def main(context):
|
def main(context):
|
||||||
print("____________________________")
|
print("____________________________")
|
||||||
|
|
||||||
#Store selection
|
# Store selection
|
||||||
utilities_uv.selection_store()
|
utilities_uv.selection_store()
|
||||||
|
|
||||||
bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
|
bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
|
||||||
uv_layers = bm.loops.layers.uv.verify()
|
uv_layers = bm.loops.layers.uv.verify()
|
||||||
|
|
||||||
edges = utilities_uv.get_selected_uv_edges(bm, uv_layers)
|
edges = utilities_uv.get_selected_uv_edges(bm, uv_layers)
|
||||||
islands = utilities_uv.getSelectionIslands()
|
islands = utilities_uv.getSelectionIslands()
|
||||||
uvs = utilities_uv.get_selected_uvs(bm, uv_layers)
|
uvs = utilities_uv.get_selected_uvs(bm, uv_layers)
|
||||||
faces = [f for island in islands for f in island ]
|
faces = [f for island in islands for f in island]
|
||||||
|
|
||||||
|
|
||||||
# Get island faces
|
# Get island faces
|
||||||
|
|
||||||
|
|
||||||
# utilities_uv.selection_restore(bm, uv_layers)
|
# utilities_uv.selection_restore(bm, uv_layers)
|
||||||
|
|
||||||
|
|
||||||
groups = get_edge_groups(bm, uv_layers, faces, edges, uvs)
|
groups = get_edge_groups(bm, uv_layers, faces, edges, uvs)
|
||||||
|
|
||||||
bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE')
|
bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE')
|
||||||
@ -78,15 +72,10 @@ def main(context):
|
|||||||
for face in faces:
|
for face in faces:
|
||||||
face.select = True
|
face.select = True
|
||||||
|
|
||||||
|
|
||||||
print("Edges {}x".format(len(edges)))
|
print("Edges {}x".format(len(edges)))
|
||||||
print("Groups {}x".format(len(groups)))
|
print("Groups {}x".format(len(groups)))
|
||||||
|
|
||||||
# Restore 3D face selection
|
# Restore 3D face selection
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Restore UV seams and clear pins
|
# Restore UV seams and clear pins
|
||||||
bpy.ops.uv.seams_from_islands()
|
bpy.ops.uv.seams_from_islands()
|
||||||
@ -94,21 +83,16 @@ def main(context):
|
|||||||
|
|
||||||
edge_sets = []
|
edge_sets = []
|
||||||
for edges in groups:
|
for edges in groups:
|
||||||
edge_sets.append( EdgeSet(bm, uv_layers, edges, faces) )
|
edge_sets.append(EdgeSet(bm, uv_layers, edges, faces))
|
||||||
# straighten_edges(bm, uv_layers, edges, faces)
|
# straighten_edges(bm, uv_layers, edges, faces)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
sorted_sets = sorted(edge_sets, key=lambda x: x.length, reverse=True)
|
sorted_sets = sorted(edge_sets, key=lambda x: x.length, reverse=True)
|
||||||
|
|
||||||
for edge_set in sorted_sets:
|
for edge_set in sorted_sets:
|
||||||
edge_set.straighten()
|
edge_set.straighten()
|
||||||
|
|
||||||
#Restore selection
|
# Restore selection
|
||||||
utilities_uv.selection_restore()
|
utilities_uv.selection_restore()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class EdgeSet:
|
class EdgeSet:
|
||||||
@ -136,11 +120,11 @@ class EdgeSet:
|
|||||||
uv1 = self.vert_to_uv[e.verts[0]][0].uv
|
uv1 = self.vert_to_uv[e.verts[0]][0].uv
|
||||||
uv2 = self.vert_to_uv[e.verts[1]][0].uv
|
uv2 = self.vert_to_uv[e.verts[1]][0].uv
|
||||||
self.edge_length[e] = (uv2 - uv1).length
|
self.edge_length[e] = (uv2 - uv1).length
|
||||||
self.length+=self.edge_length[e]
|
self.length += self.edge_length[e]
|
||||||
|
|
||||||
|
|
||||||
def straighten(self):
|
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
|
# Get edge angles in UV space
|
||||||
angles = {}
|
angles = {}
|
||||||
@ -148,17 +132,18 @@ class EdgeSet:
|
|||||||
uv1 = self.vert_to_uv[edge.verts[0]][0].uv
|
uv1 = self.vert_to_uv[edge.verts[0]][0].uv
|
||||||
uv2 = self.vert_to_uv[edge.verts[1]][0].uv
|
uv2 = self.vert_to_uv[edge.verts[1]][0].uv
|
||||||
delta = uv2 - uv1
|
delta = uv2 - uv1
|
||||||
angle = math.atan2(delta.y, delta.x)%(math.pi/2)
|
angle = math.atan2(delta.y, delta.x) % (math.pi/2)
|
||||||
if angle >= (math.pi/4):
|
if angle >= (math.pi/4):
|
||||||
angle = angle - (math.pi/2)
|
angle = angle - (math.pi/2)
|
||||||
angles[edge] = abs(angle)
|
angles[edge] = abs(angle)
|
||||||
# print("Angle {:.2f} degr".format(angle * 180 / math.pi))
|
# print("Angle {:.2f} degr".format(angle * 180 / math.pi))
|
||||||
|
|
||||||
# Pick edge with least rotation offset to U or V axis
|
# Pick edge with least rotation offset to U or V axis
|
||||||
edge_main = sorted(angles.items(), key = operator.itemgetter(1))[0][0]
|
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
|
# Rotate main edge to closest axis
|
||||||
uvs = [uv for v in edge_main.verts for uv in self.vert_to_uv[v]]
|
uvs = [uv for v in edge_main.verts for uv in self.vert_to_uv[v]]
|
||||||
bpy.ops.uv.select_all(action='DESELECT')
|
bpy.ops.uv.select_all(action='DESELECT')
|
||||||
@ -167,11 +152,12 @@ class EdgeSet:
|
|||||||
uv1 = self.vert_to_uv[edge_main.verts[0]][0].uv
|
uv1 = self.vert_to_uv[edge_main.verts[0]][0].uv
|
||||||
uv2 = self.vert_to_uv[edge_main.verts[1]][0].uv
|
uv2 = self.vert_to_uv[edge_main.verts[1]][0].uv
|
||||||
diff = uv2 - uv1
|
diff = uv2 - uv1
|
||||||
angle = math.atan2(diff.y, diff.x)%(math.pi/2)
|
angle = math.atan2(diff.y, diff.x) % (math.pi/2)
|
||||||
if angle >= (math.pi/4):
|
if angle >= (math.pi/4):
|
||||||
angle = angle - (math.pi/2)
|
angle = angle - (math.pi/2)
|
||||||
bpy.ops.uv.cursor_set(location=uv1 + diff/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
|
# Expand edges and straighten
|
||||||
count = len(self.edges)
|
count = len(self.edges)
|
||||||
@ -179,11 +165,13 @@ class EdgeSet:
|
|||||||
for i in range(count):
|
for i in range(count):
|
||||||
if(len(processed) < len(self.edges)):
|
if(len(processed) < len(self.edges)):
|
||||||
verts = set([v for e in processed for v in e.verts])
|
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)]
|
edges_expand = [e for e in self.edges if e not in processed and (
|
||||||
verts_ends = [v for e in edges_expand for v in e.verts if v in verts]
|
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:
|
if len(edges_expand) == 0:
|
||||||
continue
|
continue
|
||||||
@ -192,25 +180,31 @@ class EdgeSet:
|
|||||||
# if edge.verts[0] in verts_ends and edge.verts[1] in verts_ends:
|
# if edge.verts[0] in verts_ends and edge.verts[1] in verts_ends:
|
||||||
# print("Cancel at edge {}".format(edge.index))
|
# print("Cancel at edge {}".format(edge.index))
|
||||||
# return
|
# 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]
|
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]
|
v2 = [v for v in edge.verts if v not in verts_ends][0]
|
||||||
# direction
|
# 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_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]
|
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]:
|
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)))
|
print("verts_ends: {}x".format(len(verts_ends)))
|
||||||
processed.extend(edges_expand)
|
processed.extend(edges_expand)
|
||||||
|
|
||||||
# Select edges
|
# 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')
|
bpy.ops.uv.select_all(action='DESELECT')
|
||||||
for uv in uvs:
|
for uv in uvs:
|
||||||
uv.select = True
|
uv.select = True
|
||||||
@ -221,16 +215,10 @@ class EdgeSet:
|
|||||||
bpy.ops.uv.pin(clear=True)
|
bpy.ops.uv.pin(clear=True)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_edge_groups(bm, uv_layers, faces, edges, uvs):
|
def get_edge_groups(bm, uv_layers, faces, edges, uvs):
|
||||||
print("Get edge groups, edges {}x".format(len(edges))+"x")
|
print("Get edge groups, edges {}x".format(len(edges))+"x")
|
||||||
|
|
||||||
bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE')
|
bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE')
|
||||||
|
|
||||||
unmatched = edges.copy()
|
unmatched = edges.copy()
|
||||||
|
|
||||||
@ -253,7 +241,8 @@ def get_edge_groups(bm, uv_layers, faces, edges, uvs):
|
|||||||
if e in unmatched:
|
if e in unmatched:
|
||||||
unmatched.remove(e)
|
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
|
# return
|
||||||
# group = [edge]
|
# group = [edge]
|
||||||
@ -262,11 +251,7 @@ def get_edge_groups(bm, uv_layers, faces, edges, uvs):
|
|||||||
# unmatched.remove(e)
|
# unmatched.remove(e)
|
||||||
# group.append(edge)
|
# group.append(edge)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return groups
|
return groups
|
||||||
|
|
||||||
|
|
||||||
bpy.utils.register_class(op)
|
bpy.utils.register_class(op)
|
||||||
|
|
||||||
|
|
||||||
|
@ -9,8 +9,6 @@ import math
|
|||||||
from . import utilities_meshtex
|
from . import utilities_meshtex
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class op(bpy.types.Operator):
|
class op(bpy.types.Operator):
|
||||||
bl_idname = "uv.textools_meshtex_trim"
|
bl_idname = "uv.textools_meshtex_trim"
|
||||||
bl_label = "Trim"
|
bl_label = "Trim"
|
||||||
@ -21,12 +19,12 @@ class op(bpy.types.Operator):
|
|||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
if not bpy.context.active_object or bpy.context.active_object.mode != 'OBJECT':
|
if not bpy.context.active_object or bpy.context.active_object.mode != 'OBJECT':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if len(bpy.context.selected_objects) >= 1:
|
if len(bpy.context.selected_objects) >= 1:
|
||||||
# Find a UV mesh
|
# Find a UV mesh
|
||||||
if utilities_meshtex.find_uv_mesh(bpy.context.selected_objects):
|
if utilities_meshtex.find_uv_mesh(bpy.context.selected_objects):
|
||||||
# Find 1 or more meshes to wrap
|
# Find 1 or more meshes to wrap
|
||||||
if len( utilities_meshtex.find_texture_meshes(bpy.context.selected_objects)) > 0:
|
if len(utilities_meshtex.find_texture_meshes(bpy.context.selected_objects)) > 0:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
@ -36,42 +34,40 @@ class op(bpy.types.Operator):
|
|||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def trim(self):
|
def trim(self):
|
||||||
# Wrap the mesh texture around the
|
# Wrap the mesh texture around the
|
||||||
print("Trim Mesh Texture :)")
|
print("Trim Mesh Texture :)")
|
||||||
|
|
||||||
# Collect UV mesh
|
# Collect UV mesh
|
||||||
obj_uv = utilities_meshtex.find_uv_mesh(bpy.context.selected_objects)
|
obj_uv = utilities_meshtex.find_uv_mesh(bpy.context.selected_objects)
|
||||||
if not obj_uv:
|
if not obj_uv:
|
||||||
self.report({'ERROR_INVALID_INPUT'}, "No UV mesh found" )
|
self.report({'ERROR_INVALID_INPUT'}, "No UV mesh found")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Collect texture meshes
|
# 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:
|
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
|
return
|
||||||
|
|
||||||
# Setup Thickness
|
# Setup Thickness
|
||||||
utilities_meshtex.uv_mesh_fit(obj_uv, obj_textures)
|
utilities_meshtex.uv_mesh_fit(obj_uv, obj_textures)
|
||||||
|
|
||||||
|
|
||||||
# Apply bool modifier to trim
|
# Apply bool modifier to trim
|
||||||
for obj in obj_textures:
|
for obj in obj_textures:
|
||||||
name = "Trim UV"
|
name = "Trim UV"
|
||||||
|
|
||||||
if name in obj.modifiers:
|
if name in obj.modifiers:
|
||||||
obj.modifiers.remove( obj.modifiers[name] )
|
obj.modifiers.remove(obj.modifiers[name])
|
||||||
|
|
||||||
modifier_bool = obj.modifiers.new(name=name, type='BOOLEAN')
|
modifier_bool = obj.modifiers.new(name=name, type='BOOLEAN')
|
||||||
modifier_bool.object = obj_uv
|
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)
|
bpy.utils.register_class(op)
|
||||||
|
|
||||||
|
|
||||||
|
313
op_rectify.py
313
op_rectify.py
@ -15,7 +15,7 @@ class op(bpy.types.Operator):
|
|||||||
bl_label = "Rectify"
|
bl_label = "Rectify"
|
||||||
bl_description = "Align selected faces or verts to rectangular distribution."
|
bl_description = "Align selected faces or verts to rectangular distribution."
|
||||||
bl_options = {'REGISTER', 'UNDO'}
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
if not bpy.context.active_object:
|
if not bpy.context.active_object:
|
||||||
@ -32,34 +32,31 @@ class op(bpy.types.Operator):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
rectify(self, context)
|
rectify(self, context)
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
precision = 3
|
precision = 3
|
||||||
|
|
||||||
|
|
||||||
def rectify(self, context):
|
def rectify(self, context):
|
||||||
obj = bpy.context.active_object
|
obj = bpy.context.active_object
|
||||||
|
|
||||||
|
|
||||||
bm = bmesh.from_edit_mesh(obj.data)
|
bm = bmesh.from_edit_mesh(obj.data)
|
||||||
uv_layers = bm.loops.layers.uv.verify()
|
uv_layers = bm.loops.layers.uv.verify()
|
||||||
|
|
||||||
#Store selection
|
# Store selection
|
||||||
utilities_uv.selection_store()
|
utilities_uv.selection_store()
|
||||||
|
|
||||||
main(False)
|
main(False)
|
||||||
|
|
||||||
#Restore selection
|
# Restore selection
|
||||||
utilities_uv.selection_restore()
|
utilities_uv.selection_restore()
|
||||||
|
|
||||||
|
|
||||||
def main(square = False, snapToClosest = False):
|
def main(square=False, snapToClosest=False):
|
||||||
|
|
||||||
startTime = time.clock()
|
startTime = time.clock()
|
||||||
obj = bpy.context.active_object
|
obj = bpy.context.active_object
|
||||||
@ -68,69 +65,72 @@ def main(square = False, snapToClosest = False):
|
|||||||
uv_layers = bm.loops.layers.uv.verify()
|
uv_layers = bm.loops.layers.uv.verify()
|
||||||
# bm.faces.layers.tex.verify() # currently blender needs both layers.
|
# bm.faces.layers.tex.verify() # currently blender needs both layers.
|
||||||
|
|
||||||
face_act = bm.faces.active
|
face_act = bm.faces.active
|
||||||
targetFace = face_act
|
targetFace = face_act
|
||||||
|
|
||||||
#if len(bm.faces) > allowedFaces:
|
|
||||||
# operator.report({'ERROR'}, "selected more than " +str(allowedFaces) +" allowed faces.")
|
|
||||||
# return
|
|
||||||
|
|
||||||
edgeVerts, filteredVerts, selFaces, nonQuadFaces, vertsDict, noEdge = ListsOfVerts(uv_layers, bm)
|
# if len(bm.faces) > allowedFaces:
|
||||||
|
# operator.report({'ERROR'}, "selected more than " +str(allowedFaces) +" allowed faces.")
|
||||||
if len(filteredVerts) is 0: return
|
# return
|
||||||
if len(filteredVerts) is 1:
|
|
||||||
|
edgeVerts, filteredVerts, selFaces, nonQuadFaces, vertsDict, noEdge = ListsOfVerts(
|
||||||
|
uv_layers, bm)
|
||||||
|
|
||||||
|
if len(filteredVerts) is 0:
|
||||||
|
return
|
||||||
|
if len(filteredVerts) is 1:
|
||||||
SnapCursorToClosestSelected(filteredVerts)
|
SnapCursorToClosestSelected(filteredVerts)
|
||||||
return
|
return
|
||||||
|
|
||||||
cursorClosestTo = CursorClosestTo(filteredVerts)
|
cursorClosestTo = CursorClosestTo(filteredVerts)
|
||||||
#line is selected
|
#line is selected
|
||||||
|
|
||||||
if len(selFaces) is 0:
|
if len(selFaces) is 0:
|
||||||
if snapToClosest is True:
|
if snapToClosest is True:
|
||||||
SnapCursorToClosestSelected(filteredVerts)
|
SnapCursorToClosestSelected(filteredVerts)
|
||||||
return
|
return
|
||||||
|
|
||||||
VertsDictForLine(uv_layers, bm, filteredVerts, vertsDict)
|
VertsDictForLine(uv_layers, bm, filteredVerts, vertsDict)
|
||||||
|
|
||||||
if AreVectsLinedOnAxis(filteredVerts) is False:
|
if AreVectsLinedOnAxis(filteredVerts) is False:
|
||||||
ScaleTo0OnAxisAndCursor(filteredVerts, vertsDict, cursorClosestTo)
|
ScaleTo0OnAxisAndCursor(filteredVerts, vertsDict, cursorClosestTo)
|
||||||
return SuccessFinished(me, startTime)
|
return SuccessFinished(me, startTime)
|
||||||
|
|
||||||
MakeEqualDistanceBetweenVertsInLine(filteredVerts, vertsDict, cursorClosestTo)
|
MakeEqualDistanceBetweenVertsInLine(
|
||||||
|
filteredVerts, vertsDict, cursorClosestTo)
|
||||||
return SuccessFinished(me, startTime)
|
return SuccessFinished(me, startTime)
|
||||||
|
|
||||||
|
# else:
|
||||||
#else:
|
|
||||||
|
# active face checks
|
||||||
#active face checks
|
|
||||||
if targetFace is None or targetFace.select is False or len(targetFace.verts) is not 4:
|
if targetFace is None or targetFace.select is False or len(targetFace.verts) is not 4:
|
||||||
targetFace = selFaces[0]
|
targetFace = selFaces[0]
|
||||||
else:
|
else:
|
||||||
for l in targetFace.loops:
|
for l in targetFace.loops:
|
||||||
if l[uv_layers].select is False:
|
if l[uv_layers].select is False:
|
||||||
targetFace = selFaces[0]
|
targetFace = selFaces[0]
|
||||||
break
|
break
|
||||||
|
|
||||||
ShapeFace(uv_layers, operator, targetFace, vertsDict, square)
|
ShapeFace(uv_layers, operator, targetFace, vertsDict, square)
|
||||||
|
|
||||||
for nf in nonQuadFaces:
|
for nf in nonQuadFaces:
|
||||||
for l in nf.loops:
|
for l in nf.loops:
|
||||||
luv = l[uv_layers]
|
luv = l[uv_layers]
|
||||||
luv.select = False
|
luv.select = False
|
||||||
|
|
||||||
if square: FollowActiveUV(operator, me, targetFace, selFaces, 'EVEN')
|
if square:
|
||||||
else: FollowActiveUV(operator, me, targetFace, selFaces)
|
FollowActiveUV(operator, me, targetFace, selFaces, 'EVEN')
|
||||||
|
else:
|
||||||
|
FollowActiveUV(operator, me, targetFace, selFaces)
|
||||||
|
|
||||||
if noEdge is False:
|
if noEdge is False:
|
||||||
#edge has ripped so we connect it back
|
# edge has ripped so we connect it back
|
||||||
for ev in edgeVerts:
|
for ev in edgeVerts:
|
||||||
key = (round(ev.uv.x, precision), round(ev.uv.y, precision))
|
key = (round(ev.uv.x, precision), round(ev.uv.y, precision))
|
||||||
if key in vertsDict:
|
if key in vertsDict:
|
||||||
ev.uv = vertsDict[key][0].uv
|
ev.uv = vertsDict[key][0].uv
|
||||||
ev.select = True
|
ev.select = True
|
||||||
|
|
||||||
return SuccessFinished(me, startTime)
|
|
||||||
|
|
||||||
|
return SuccessFinished(me, startTime)
|
||||||
|
|
||||||
|
|
||||||
def ListsOfVerts(uv_layers, bm):
|
def ListsOfVerts(uv_layers, bm):
|
||||||
@ -139,50 +139,52 @@ def ListsOfVerts(uv_layers, bm):
|
|||||||
filteredVerts = []
|
filteredVerts = []
|
||||||
selFaces = []
|
selFaces = []
|
||||||
nonQuadFaces = []
|
nonQuadFaces = []
|
||||||
vertsDict = defaultdict(list) #dict
|
vertsDict = defaultdict(list) # dict
|
||||||
|
|
||||||
for f in bm.faces:
|
for f in bm.faces:
|
||||||
isFaceSel = True
|
isFaceSel = True
|
||||||
facesEdgeVerts = []
|
facesEdgeVerts = []
|
||||||
if (f.select == False):
|
if (f.select == False):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
#collect edge verts if any
|
# collect edge verts if any
|
||||||
for l in f.loops:
|
for l in f.loops:
|
||||||
luv = l[uv_layers]
|
luv = l[uv_layers]
|
||||||
if luv.select is True:
|
if luv.select is True:
|
||||||
facesEdgeVerts.append(luv)
|
facesEdgeVerts.append(luv)
|
||||||
else: isFaceSel = False
|
else:
|
||||||
|
isFaceSel = False
|
||||||
|
|
||||||
allEdgeVerts.extend(facesEdgeVerts)
|
allEdgeVerts.extend(facesEdgeVerts)
|
||||||
if isFaceSel:
|
if isFaceSel:
|
||||||
if len(f.verts) is not 4:
|
if len(f.verts) is not 4:
|
||||||
nonQuadFaces.append(f)
|
nonQuadFaces.append(f)
|
||||||
edgeVerts.extend(facesEdgeVerts)
|
edgeVerts.extend(facesEdgeVerts)
|
||||||
else:
|
else:
|
||||||
selFaces.append(f)
|
selFaces.append(f)
|
||||||
|
|
||||||
for l in f.loops:
|
for l in f.loops:
|
||||||
luv = l[uv_layers]
|
luv = l[uv_layers]
|
||||||
x = round(luv.uv.x, precision)
|
x = round(luv.uv.x, precision)
|
||||||
y = round(luv.uv.y, precision)
|
y = round(luv.uv.y, precision)
|
||||||
vertsDict[(x, y)].append(luv)
|
vertsDict[(x, y)].append(luv)
|
||||||
|
|
||||||
else: edgeVerts.extend(facesEdgeVerts)
|
else:
|
||||||
|
edgeVerts.extend(facesEdgeVerts)
|
||||||
|
|
||||||
noEdge = False
|
noEdge = False
|
||||||
if len(edgeVerts) is 0:
|
if len(edgeVerts) is 0:
|
||||||
noEdge = True
|
noEdge = True
|
||||||
edgeVerts.extend(allEdgeVerts)
|
edgeVerts.extend(allEdgeVerts)
|
||||||
|
|
||||||
if len(selFaces) is 0:
|
if len(selFaces) is 0:
|
||||||
for ev in edgeVerts:
|
for ev in edgeVerts:
|
||||||
if ListQuasiContainsVect(filteredVerts, ev) is False:
|
if ListQuasiContainsVect(filteredVerts, ev) is False:
|
||||||
filteredVerts.append(ev)
|
filteredVerts.append(ev)
|
||||||
else: filteredVerts = edgeVerts
|
else:
|
||||||
|
filteredVerts = edgeVerts
|
||||||
return edgeVerts, filteredVerts, selFaces, nonQuadFaces, vertsDict, noEdge
|
|
||||||
|
|
||||||
|
return edgeVerts, filteredVerts, selFaces, nonQuadFaces, vertsDict, noEdge
|
||||||
|
|
||||||
|
|
||||||
def ListQuasiContainsVect(list, vect):
|
def ListQuasiContainsVect(list, vect):
|
||||||
@ -192,28 +194,23 @@ def ListQuasiContainsVect(list, vect):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def SnapCursorToClosestSelected(filteredVerts):
|
def SnapCursorToClosestSelected(filteredVerts):
|
||||||
#TODO: snap to closest selected
|
# TODO: snap to closest selected
|
||||||
if len(filteredVerts) is 1:
|
if len(filteredVerts) is 1:
|
||||||
SetAll2dCursorsTo(filteredVerts[0].uv.x, filteredVerts[0].uv.y)
|
SetAll2dCursorsTo(filteredVerts[0].uv.x, filteredVerts[0].uv.y)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def VertsDictForLine(uv_layers, bm, selVerts, vertsDict):
|
def VertsDictForLine(uv_layers, bm, selVerts, vertsDict):
|
||||||
for f in bm.faces:
|
for f in bm.faces:
|
||||||
for l in f.loops:
|
for l in f.loops:
|
||||||
luv = l[uv_layers]
|
luv = l[uv_layers]
|
||||||
if luv.select is True:
|
if luv.select is True:
|
||||||
x = round(luv.uv.x, precision)
|
x = round(luv.uv.x, precision)
|
||||||
y = round(luv.uv.y, precision)
|
y = round(luv.uv.y, precision)
|
||||||
|
|
||||||
vertsDict[(x, y)].append(luv)
|
|
||||||
|
|
||||||
|
vertsDict[(x, y)].append(luv)
|
||||||
|
|
||||||
|
|
||||||
def AreVectsLinedOnAxis(verts):
|
def AreVectsLinedOnAxis(verts):
|
||||||
@ -227,191 +224,189 @@ def AreVectsLinedOnAxis(verts):
|
|||||||
areLinedX = False
|
areLinedX = False
|
||||||
if abs(valY - v.uv.y) > allowedError:
|
if abs(valY - v.uv.y) > allowedError:
|
||||||
areLinedY = False
|
areLinedY = False
|
||||||
return areLinedX or areLinedY
|
return areLinedX or areLinedY
|
||||||
|
|
||||||
|
|
||||||
|
def ScaleTo0OnAxisAndCursor(filteredVerts, vertsDict, startv=None, horizontal=None):
|
||||||
|
|
||||||
def ScaleTo0OnAxisAndCursor(filteredVerts, vertsDict, startv = None, horizontal = None):
|
|
||||||
|
|
||||||
verts = filteredVerts
|
verts = filteredVerts
|
||||||
verts.sort(key=lambda x: x.uv[0]) #sort by .x
|
verts.sort(key=lambda x: x.uv[0]) # sort by .x
|
||||||
|
|
||||||
first = verts[0]
|
first = verts[0]
|
||||||
last = verts[len(verts)-1]
|
last = verts[len(verts)-1]
|
||||||
|
|
||||||
if horizontal is None:
|
if horizontal is None:
|
||||||
horizontal = True
|
horizontal = True
|
||||||
if ((last.uv.x - first.uv.x) >0.0009):
|
if ((last.uv.x - first.uv.x) > 0.0009):
|
||||||
slope = (last.uv.y - first.uv.y)/(last.uv.x - first.uv.x)
|
slope = (last.uv.y - first.uv.y)/(last.uv.x - first.uv.x)
|
||||||
if (slope > 1) or (slope <-1):
|
if (slope > 1) or (slope < -1):
|
||||||
horizontal = False
|
horizontal = False
|
||||||
else:
|
else:
|
||||||
horizontal = False
|
horizontal = False
|
||||||
|
|
||||||
if horizontal is True:
|
if horizontal is True:
|
||||||
if startv is None:
|
if startv is None:
|
||||||
startv = first
|
startv = first
|
||||||
|
|
||||||
SetAll2dCursorsTo(startv.uv.x, startv.uv.y)
|
SetAll2dCursorsTo(startv.uv.x, startv.uv.y)
|
||||||
#scale to 0 on Y
|
# scale to 0 on Y
|
||||||
ScaleTo0('Y')
|
ScaleTo0('Y')
|
||||||
return
|
return
|
||||||
|
|
||||||
else:
|
else:
|
||||||
verts.sort(key=lambda x: x.uv[1]) #sort by .y
|
verts.sort(key=lambda x: x.uv[1]) # sort by .y
|
||||||
verts.reverse() #reverse because y values drop from up to down
|
verts.reverse() # reverse because y values drop from up to down
|
||||||
first = verts[0]
|
first = verts[0]
|
||||||
last = verts[len(verts)-1]
|
last = verts[len(verts)-1]
|
||||||
if startv is None:
|
if startv is None:
|
||||||
startv = first
|
startv = first
|
||||||
|
|
||||||
SetAll2dCursorsTo(startv.uv.x, startv.uv.y)
|
SetAll2dCursorsTo(startv.uv.x, startv.uv.y)
|
||||||
#scale to 0 on X
|
# scale to 0 on X
|
||||||
ScaleTo0('X')
|
ScaleTo0('X')
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def SetAll2dCursorsTo(x, y):
|
||||||
def SetAll2dCursorsTo(x,y):
|
|
||||||
last_area = bpy.context.area.type
|
last_area = bpy.context.area.type
|
||||||
bpy.context.area.type = 'IMAGE_EDITOR'
|
bpy.context.area.type = 'IMAGE_EDITOR'
|
||||||
|
|
||||||
bpy.ops.uv.cursor_set(location=(x, y))
|
bpy.ops.uv.cursor_set(location=(x, y))
|
||||||
|
|
||||||
bpy.context.area.type = last_area
|
bpy.context.area.type = last_area
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def CursorClosestTo(verts, allowedError=0.025):
|
||||||
def CursorClosestTo(verts, allowedError = 0.025):
|
|
||||||
ratioX, ratioY = ImageRatio()
|
ratioX, ratioY = ImageRatio()
|
||||||
|
|
||||||
#any length that is certantly not smaller than distance of the closest
|
# any length that is certantly not smaller than distance of the closest
|
||||||
min = 1000
|
min = 1000
|
||||||
minV = verts[0]
|
minV = verts[0]
|
||||||
for v in verts:
|
for v in verts:
|
||||||
if v is None: continue
|
if v is None:
|
||||||
|
continue
|
||||||
for area in bpy.context.screen.areas:
|
for area in bpy.context.screen.areas:
|
||||||
if area.type == 'IMAGE_EDITOR':
|
if area.type == 'IMAGE_EDITOR':
|
||||||
loc = area.spaces[0].cursor_location
|
loc = area.spaces[0].cursor_location
|
||||||
hyp = hypot(loc.x/ratioX -v.uv.x, loc.y/ratioY -v.uv.y)
|
hyp = hypot(loc.x/ratioX - v.uv.x, loc.y/ratioY - v.uv.y)
|
||||||
if (hyp < min):
|
if (hyp < min):
|
||||||
min = hyp
|
min = hyp
|
||||||
minV = v
|
minV = v
|
||||||
|
|
||||||
if min is not 1000:
|
if min is not 1000:
|
||||||
return minV
|
return minV
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def SuccessFinished(me, startTime):
|
def SuccessFinished(me, startTime):
|
||||||
#use for backtrack of steps
|
# use for backtrack of steps
|
||||||
#bpy.ops.ed.undo_push()
|
# bpy.ops.ed.undo_push()
|
||||||
bmesh.update_edit_mesh(me)
|
bmesh.update_edit_mesh(me)
|
||||||
#elapsed = round(time.clock()-startTime, 2)
|
#elapsed = round(time.clock()-startTime, 2)
|
||||||
#if (elapsed >= 0.05): operator.report({'INFO'}, "UvSquares finished, elapsed:", elapsed, "s.")
|
#if (elapsed >= 0.05): operator.report({'INFO'}, "UvSquares finished, elapsed:", elapsed, "s.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def ShapeFace(uv_layers, operator, targetFace, vertsDict, square):
|
def ShapeFace(uv_layers, operator, targetFace, vertsDict, square):
|
||||||
corners = []
|
corners = []
|
||||||
for l in targetFace.loops:
|
for l in targetFace.loops:
|
||||||
luv = l[uv_layers]
|
luv = l[uv_layers]
|
||||||
corners.append(luv)
|
corners.append(luv)
|
||||||
|
|
||||||
if len(corners) is not 4:
|
if len(corners) is not 4:
|
||||||
#operator.report({'ERROR'}, "bla")
|
#operator.report({'ERROR'}, "bla")
|
||||||
return
|
return
|
||||||
|
|
||||||
lucv, ldcv, rucv, rdcv = Corners(corners)
|
lucv, ldcv, rucv, rdcv = Corners(corners)
|
||||||
|
|
||||||
cct = CursorClosestTo([lucv, ldcv, rdcv, rucv])
|
cct = CursorClosestTo([lucv, ldcv, rdcv, rucv])
|
||||||
if cct is None:
|
if cct is None:
|
||||||
cct = lucv
|
cct = lucv
|
||||||
|
|
||||||
MakeUvFaceEqualRectangle(vertsDict, lucv, rucv, rdcv, ldcv, cct, square)
|
MakeUvFaceEqualRectangle(vertsDict, lucv, rucv, rdcv, ldcv, cct, square)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def MakeUvFaceEqualRectangle(vertsDict, lucv, rucv, rdcv, ldcv, startv, square=False):
|
||||||
def MakeUvFaceEqualRectangle(vertsDict, lucv, rucv, rdcv, ldcv, startv, square = False):
|
|
||||||
ratioX, ratioY = ImageRatio()
|
ratioX, ratioY = ImageRatio()
|
||||||
ratio = ratioX/ratioY
|
ratio = ratioX/ratioY
|
||||||
|
|
||||||
if startv is None: startv = lucv.uv
|
if startv is None:
|
||||||
elif AreVertsQuasiEqual(startv, rucv): startv = rucv.uv
|
startv = lucv.uv
|
||||||
elif AreVertsQuasiEqual(startv, rdcv): startv = rdcv.uv
|
elif AreVertsQuasiEqual(startv, rucv):
|
||||||
elif AreVertsQuasiEqual(startv, ldcv): startv = ldcv.uv
|
startv = rucv.uv
|
||||||
else: startv = lucv.uv
|
elif AreVertsQuasiEqual(startv, rdcv):
|
||||||
|
startv = rdcv.uv
|
||||||
|
elif AreVertsQuasiEqual(startv, ldcv):
|
||||||
|
startv = ldcv.uv
|
||||||
|
else:
|
||||||
|
startv = lucv.uv
|
||||||
|
|
||||||
lucv = lucv.uv
|
lucv = lucv.uv
|
||||||
rucv = rucv.uv
|
rucv = rucv.uv
|
||||||
rdcv = rdcv.uv
|
rdcv = rdcv.uv
|
||||||
ldcv = ldcv.uv
|
ldcv = ldcv.uv
|
||||||
|
|
||||||
if (startv == lucv):
|
if (startv == lucv):
|
||||||
finalScaleX = hypotVert(lucv, rucv)
|
finalScaleX = hypotVert(lucv, rucv)
|
||||||
finalScaleY = hypotVert(lucv, ldcv)
|
finalScaleY = hypotVert(lucv, ldcv)
|
||||||
currRowX = lucv.x
|
currRowX = lucv.x
|
||||||
currRowY = lucv.y
|
currRowY = lucv.y
|
||||||
|
|
||||||
elif (startv == rucv):
|
elif (startv == rucv):
|
||||||
finalScaleX = hypotVert(rucv, lucv)
|
finalScaleX = hypotVert(rucv, lucv)
|
||||||
finalScaleY = hypotVert(rucv, rdcv)
|
finalScaleY = hypotVert(rucv, rdcv)
|
||||||
currRowX = rucv.x - finalScaleX
|
currRowX = rucv.x - finalScaleX
|
||||||
currRowY = rucv.y
|
currRowY = rucv.y
|
||||||
|
|
||||||
elif (startv == rdcv):
|
elif (startv == rdcv):
|
||||||
finalScaleX = hypotVert(rdcv, ldcv)
|
finalScaleX = hypotVert(rdcv, ldcv)
|
||||||
finalScaleY = hypotVert(rdcv, rucv)
|
finalScaleY = hypotVert(rdcv, rucv)
|
||||||
currRowX = rdcv.x - finalScaleX
|
currRowX = rdcv.x - finalScaleX
|
||||||
currRowY = rdcv.y + finalScaleY
|
currRowY = rdcv.y + finalScaleY
|
||||||
|
|
||||||
else:
|
else:
|
||||||
finalScaleX = hypotVert(ldcv, rdcv)
|
finalScaleX = hypotVert(ldcv, rdcv)
|
||||||
finalScaleY = hypotVert(ldcv, lucv)
|
finalScaleY = hypotVert(ldcv, lucv)
|
||||||
currRowX = ldcv.x
|
currRowX = ldcv.x
|
||||||
currRowY = ldcv.y +finalScaleY
|
currRowY = ldcv.y + finalScaleY
|
||||||
|
|
||||||
if square: finalScaleY = finalScaleX*ratio
|
if square:
|
||||||
|
finalScaleY = finalScaleX*ratio
|
||||||
#lucv, rucv
|
#lucv, rucv
|
||||||
x = round(lucv.x, precision)
|
x = round(lucv.x, precision)
|
||||||
y = round(lucv.y, precision)
|
y = round(lucv.y, precision)
|
||||||
for v in vertsDict[(x,y)]:
|
for v in vertsDict[(x, y)]:
|
||||||
v.uv.x = currRowX
|
v.uv.x = currRowX
|
||||||
v.uv.y = currRowY
|
v.uv.y = currRowY
|
||||||
|
|
||||||
x = round(rucv.x, precision)
|
x = round(rucv.x, precision)
|
||||||
y = round(rucv.y, precision)
|
y = round(rucv.y, precision)
|
||||||
for v in vertsDict[(x,y)]:
|
for v in vertsDict[(x, y)]:
|
||||||
v.uv.x = currRowX + finalScaleX
|
v.uv.x = currRowX + finalScaleX
|
||||||
v.uv.y = currRowY
|
v.uv.y = currRowY
|
||||||
|
|
||||||
#rdcv, ldcv
|
#rdcv, ldcv
|
||||||
x = round(rdcv.x, precision)
|
x = round(rdcv.x, precision)
|
||||||
y = round(rdcv.y, precision)
|
y = round(rdcv.y, precision)
|
||||||
for v in vertsDict[(x,y)]:
|
for v in vertsDict[(x, y)]:
|
||||||
v.uv.x = currRowX + finalScaleX
|
v.uv.x = currRowX + finalScaleX
|
||||||
v.uv.y = currRowY - finalScaleY
|
v.uv.y = currRowY - finalScaleY
|
||||||
|
|
||||||
x = round(ldcv.x, precision)
|
x = round(ldcv.x, precision)
|
||||||
y = round(ldcv.y, precision)
|
y = round(ldcv.y, precision)
|
||||||
for v in vertsDict[(x,y)]:
|
for v in vertsDict[(x, y)]:
|
||||||
v.uv.x = currRowX
|
v.uv.x = currRowX
|
||||||
v.uv.y = currRowY - finalScaleY
|
v.uv.y = currRowY - finalScaleY
|
||||||
|
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def FollowActiveUV(operator, me, f_act, faces, EXTEND_MODE='LENGTH_AVERAGE'):
|
||||||
def FollowActiveUV(operator, me, f_act, faces, EXTEND_MODE = 'LENGTH_AVERAGE'):
|
|
||||||
bm = bmesh.from_edit_mesh(me)
|
bm = bmesh.from_edit_mesh(me)
|
||||||
uv_act = bm.loops.layers.uv.active
|
uv_act = bm.loops.layers.uv.active
|
||||||
|
|
||||||
# our own local walker
|
# our own local walker
|
||||||
def walk_face_init(faces, f_act):
|
def walk_face_init(faces, f_act):
|
||||||
# first tag all faces True (so we dont uvmap them)
|
# first tag all faces True (so we dont uvmap them)
|
||||||
@ -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]
|
l_b_uv = [l[uv_act].uv for l in l_b]
|
||||||
|
|
||||||
if EXTEND_MODE == 'LENGTH_AVERAGE':
|
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':
|
elif EXTEND_MODE == 'LENGTH':
|
||||||
a0, b0, c0 = l_a[3].vert.co, l_a[0].vert.co, l_b[3].vert.co
|
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
|
a1, b1, c1 = l_a[2].vert.co, l_a[1].vert.co, l_b[2].vert.co
|
||||||
@ -541,11 +537,12 @@ def FollowActiveUV(operator, me, f_act, faces, EXTEND_MODE = 'LENGTH_AVERAGE'):
|
|||||||
|
|
||||||
if EXTEND_MODE == 'LENGTH_AVERAGE':
|
if EXTEND_MODE == 'LENGTH_AVERAGE':
|
||||||
bm.edges.index_update()
|
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:
|
for f in faces:
|
||||||
# we know its a quad
|
# we know its a quad
|
||||||
l_quad = f.loops[:]
|
l_quad = f.loops[:]
|
||||||
l_pair_a = (l_quad[0], l_quad[2])
|
l_pair_a = (l_quad[0], l_quad[2])
|
||||||
l_pair_b = (l_quad[1], l_quad[3])
|
l_pair_b = (l_quad[1], l_quad[3])
|
||||||
|
|
||||||
@ -564,7 +561,8 @@ def FollowActiveUV(operator, me, f_act, faces, EXTEND_MODE = 'LENGTH_AVERAGE'):
|
|||||||
edge_length_accum += e.calc_length()
|
edge_length_accum += e.calc_length()
|
||||||
edge_length_total += 1
|
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
|
# done with average length
|
||||||
# ------------------------
|
# ------------------------
|
||||||
@ -577,7 +575,7 @@ def FollowActiveUV(operator, me, f_act, faces, EXTEND_MODE = 'LENGTH_AVERAGE'):
|
|||||||
|
|
||||||
|
|
||||||
def ImageRatio():
|
def ImageRatio():
|
||||||
ratioX, ratioY = 256,256
|
ratioX, ratioY = 256, 256
|
||||||
for a in bpy.context.screen.areas:
|
for a in bpy.context.screen.areas:
|
||||||
if a.type == 'IMAGE_EDITOR':
|
if a.type == 'IMAGE_EDITOR':
|
||||||
img = a.spaces[0].image
|
img = a.spaces[0].image
|
||||||
@ -587,19 +585,18 @@ def ImageRatio():
|
|||||||
return ratioX, ratioY
|
return ratioX, ratioY
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def Corners(corners):
|
def Corners(corners):
|
||||||
firstHighest = corners[0]
|
firstHighest = corners[0]
|
||||||
for c in corners:
|
for c in corners:
|
||||||
if c.uv.y > firstHighest.uv.y:
|
if c.uv.y > firstHighest.uv.y:
|
||||||
firstHighest = c
|
firstHighest = c
|
||||||
corners.remove(firstHighest)
|
corners.remove(firstHighest)
|
||||||
|
|
||||||
secondHighest = corners[0]
|
secondHighest = corners[0]
|
||||||
for c in corners:
|
for c in corners:
|
||||||
if (c.uv.y > secondHighest.uv.y):
|
if (c.uv.y > secondHighest.uv.y):
|
||||||
secondHighest = c
|
secondHighest = c
|
||||||
|
|
||||||
if firstHighest.uv.x < secondHighest.uv.x:
|
if firstHighest.uv.x < secondHighest.uv.x:
|
||||||
leftUp = firstHighest
|
leftUp = firstHighest
|
||||||
rightUp = secondHighest
|
rightUp = secondHighest
|
||||||
@ -607,31 +604,29 @@ def Corners(corners):
|
|||||||
leftUp = secondHighest
|
leftUp = secondHighest
|
||||||
rightUp = firstHighest
|
rightUp = firstHighest
|
||||||
corners.remove(secondHighest)
|
corners.remove(secondHighest)
|
||||||
|
|
||||||
firstLowest = corners[0]
|
firstLowest = corners[0]
|
||||||
secondLowest = corners[1]
|
secondLowest = corners[1]
|
||||||
|
|
||||||
if firstLowest.uv.x < secondLowest.uv.x:
|
if firstLowest.uv.x < secondLowest.uv.x:
|
||||||
leftDown = firstLowest
|
leftDown = firstLowest
|
||||||
rightDown = secondLowest
|
rightDown = secondLowest
|
||||||
else:
|
else:
|
||||||
leftDown = secondLowest
|
leftDown = secondLowest
|
||||||
rightDown = firstLowest
|
rightDown = firstLowest
|
||||||
|
|
||||||
return leftUp, leftDown, rightUp, rightDown
|
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:
|
||||||
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 True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def hypotVert(v1, v2):
|
def hypotVert(v1, v2):
|
||||||
hyp = hypot(v1.x - v2.x, v1.y - v2.y)
|
hyp = hypot(v1.x - v2.x, v1.y - v2.y)
|
||||||
return hyp
|
return hyp
|
||||||
|
|
||||||
bpy.utils.register_class(op)
|
|
||||||
|
bpy.utils.register_class(op)
|
||||||
|
@ -9,43 +9,43 @@ from collections import defaultdict
|
|||||||
from . import utilities_texel
|
from . import utilities_texel
|
||||||
from . import utilities_uv
|
from . import utilities_uv
|
||||||
|
|
||||||
|
|
||||||
class op(bpy.types.Operator):
|
class op(bpy.types.Operator):
|
||||||
bl_idname = "uv.textools_texel_density_set"
|
bl_idname = "uv.textools_texel_density_set"
|
||||||
bl_label = "Set Texel size"
|
bl_label = "Set Texel size"
|
||||||
bl_description = "Apply texel density by scaling the UV's to match the ratio"
|
bl_description = "Apply texel density by scaling the UV's to match the ratio"
|
||||||
bl_options = {'REGISTER', 'UNDO'}
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
|
|
||||||
if not bpy.context.active_object:
|
if not bpy.context.active_object:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if len(bpy.context.selected_objects) == 0:
|
if len(bpy.context.selected_objects) == 0:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if bpy.context.active_object.type != 'MESH':
|
if bpy.context.active_object.type != 'MESH':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#Only in UV editor mode
|
# Only in UV editor mode
|
||||||
if bpy.context.area.type != 'IMAGE_EDITOR':
|
if bpy.context.area.type != 'IMAGE_EDITOR':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#Requires UV map
|
# Requires UV map
|
||||||
if not bpy.context.object.data.uv_layers:
|
if not bpy.context.object.data.uv_layers:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# if bpy.context.object.mode == 'EDIT':
|
# if bpy.context.object.mode == 'EDIT':
|
||||||
# # In edit mode requires face select mode
|
# # In edit mode requires face select mode
|
||||||
# if bpy.context.scene.tool_settings.mesh_select_mode[2] == False:
|
# if bpy.context.scene.tool_settings.mesh_select_mode[2] == False:
|
||||||
# return False
|
# return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
set_texel_density(
|
set_texel_density(
|
||||||
self,
|
self,
|
||||||
context,
|
context,
|
||||||
bpy.context.scene.texToolsSettings.texel_mode_scale,
|
bpy.context.scene.texToolsSettings.texel_mode_scale,
|
||||||
bpy.context.scene.texToolsSettings.texel_density
|
bpy.context.scene.texToolsSettings.texel_density
|
||||||
@ -53,7 +53,6 @@ class op(bpy.types.Operator):
|
|||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def set_texel_density(self, context, mode, density):
|
def set_texel_density(self, context, mode, density):
|
||||||
print("Set texel density!")
|
print("Set texel density!")
|
||||||
|
|
||||||
@ -61,10 +60,9 @@ def set_texel_density(self, context, mode, density):
|
|||||||
is_sync = bpy.context.scene.tool_settings.use_uv_select_sync
|
is_sync = bpy.context.scene.tool_settings.use_uv_select_sync
|
||||||
object_faces = utilities_texel.get_selected_object_faces()
|
object_faces = utilities_texel.get_selected_object_faces()
|
||||||
|
|
||||||
|
|
||||||
# Warning: No valid input objects
|
# Warning: No valid input objects
|
||||||
if len(object_faces) == 0:
|
if len(object_faces) == 0:
|
||||||
self.report({'ERROR_INVALID_INPUT'}, "No valid meshes or UV maps" )
|
self.report({'ERROR_INVALID_INPUT'}, "No valid meshes or UV maps")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Collect Images / textures
|
# Collect Images / textures
|
||||||
@ -76,15 +74,15 @@ def set_texel_density(self, context, mode, density):
|
|||||||
|
|
||||||
# Warning: No valid images
|
# Warning: No valid images
|
||||||
if len(object_images) == 0:
|
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
|
return
|
||||||
|
|
||||||
|
|
||||||
for obj in object_faces:
|
for obj in object_faces:
|
||||||
bpy.ops.object.mode_set(mode='OBJECT')
|
bpy.ops.object.mode_set(mode='OBJECT')
|
||||||
bpy.ops.object.select_all(action='DESELECT')
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
bpy.context.view_layer.objects.active = obj
|
bpy.context.view_layer.objects.active = obj
|
||||||
obj.select_set( state = True, view_layer = None)
|
obj.select_set(state=True, view_layer=None)
|
||||||
|
|
||||||
# Find image of object
|
# Find image of object
|
||||||
image = object_images[obj]
|
image = object_images[obj]
|
||||||
@ -118,35 +116,37 @@ def set_texel_density(self, context, mode, density):
|
|||||||
|
|
||||||
print("group_faces {}x".format(len(group_faces)))
|
print("group_faces {}x".format(len(group_faces)))
|
||||||
|
|
||||||
|
|
||||||
for group in group_faces:
|
for group in group_faces:
|
||||||
# Get triangle areas
|
# Get triangle areas
|
||||||
sum_area_vt = 0
|
sum_area_vt = 0
|
||||||
sum_area_uv = 0
|
sum_area_uv = 0
|
||||||
for face in group:
|
for face in group:
|
||||||
# Triangle Verts
|
# Triangle Verts
|
||||||
triangle_uv = [loop[uv_layers].uv for loop in face.loops ]
|
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
|
# Triangle Areas
|
||||||
face_area_vt = utilities_texel.get_area_triangle(
|
face_area_vt = utilities_texel.get_area_triangle(
|
||||||
triangle_vt[0],
|
triangle_vt[0],
|
||||||
triangle_vt[1],
|
triangle_vt[1],
|
||||||
triangle_vt[2]
|
triangle_vt[2]
|
||||||
)
|
)
|
||||||
face_area_uv = utilities_texel.get_area_triangle_uv(
|
face_area_uv = utilities_texel.get_area_triangle_uv(
|
||||||
triangle_uv[0],
|
triangle_uv[0],
|
||||||
triangle_uv[1],
|
triangle_uv[1],
|
||||||
triangle_uv[2],
|
triangle_uv[2],
|
||||||
image.size[0],
|
image.size[0],
|
||||||
image.size[1]
|
image.size[1]
|
||||||
)
|
)
|
||||||
|
|
||||||
sum_area_vt+= math.sqrt( face_area_vt )
|
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
|
# 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
|
scale = 0
|
||||||
if density > 0 and sum_area_uv > 0 and sum_area_vt > 0:
|
if density > 0 and sum_area_uv > 0 and sum_area_vt > 0:
|
||||||
scale = density / (sum_area_uv / sum_area_vt)
|
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
|
loop[uv_layers].select = True
|
||||||
|
|
||||||
print("Scale: {} {}x".format(scale, len(group)))
|
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
|
# Restore selection
|
||||||
utilities_uv.selection_restore()
|
utilities_uv.selection_restore()
|
||||||
@ -175,7 +176,7 @@ def set_texel_density(self, context, mode, density):
|
|||||||
bpy.ops.object.mode_set(mode='OBJECT')
|
bpy.ops.object.mode_set(mode='OBJECT')
|
||||||
bpy.ops.object.select_all(action='DESELECT')
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
for obj in object_faces:
|
for obj in object_faces:
|
||||||
obj.select_set( state = True, view_layer = None)
|
obj.select_set(state=True, view_layer=None)
|
||||||
bpy.context.view_layer.objects.active = list(object_faces.keys())[0]
|
bpy.context.view_layer.objects.active = list(object_faces.keys())[0]
|
||||||
|
|
||||||
# Restore edit mode
|
# Restore edit mode
|
||||||
@ -186,4 +187,5 @@ def set_texel_density(self, context, mode, density):
|
|||||||
if is_sync:
|
if is_sync:
|
||||||
bpy.context.scene.tool_settings.use_uv_select_sync = True
|
bpy.context.scene.tool_settings.use_uv_select_sync = True
|
||||||
|
|
||||||
|
|
||||||
bpy.utils.register_class(op)
|
bpy.utils.register_class(op)
|
||||||
|
@ -2,7 +2,9 @@ import bpy
|
|||||||
import bmesh
|
import bmesh
|
||||||
import operator
|
import operator
|
||||||
import math
|
import math
|
||||||
import os, sys, subprocess
|
import os
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
|
||||||
from . import settings
|
from . import settings
|
||||||
from . import utilities_bake
|
from . import utilities_bake
|
||||||
@ -13,27 +15,26 @@ class op(bpy.types.Operator):
|
|||||||
bl_label = "Open Texture"
|
bl_label = "Open Texture"
|
||||||
bl_description = "Open the texture on the system"
|
bl_description = "Open the texture on the system"
|
||||||
|
|
||||||
name : bpy.props.StringProperty(
|
name: bpy.props.StringProperty(
|
||||||
name="image name",
|
name="image name",
|
||||||
default = ""
|
default=""
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
open_texture(self, context)
|
open_texture(self, context)
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def open_texture(self, context):
|
def open_texture(self, context):
|
||||||
|
|
||||||
print("Info")
|
print("Info")
|
||||||
if self.name in bpy.data.images:
|
if self.name in bpy.data.images:
|
||||||
image = bpy.data.images[self.name]
|
image = bpy.data.images[self.name]
|
||||||
|
|
||||||
if image.filepath != "":
|
if image.filepath != "":
|
||||||
path = bpy.path.abspath(image.filepath)
|
path = bpy.path.abspath(image.filepath)
|
||||||
# https://meshlogic.github.io/posts/blender/addons/extra-image-list/
|
# https://meshlogic.github.io/posts/blender/addons/extra-image-list/
|
||||||
@ -43,8 +44,8 @@ def open_texture(self, context):
|
|||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
os.startfile(path)
|
os.startfile(path)
|
||||||
else:
|
else:
|
||||||
opener ="open" if sys.platform == "darwin" else "xdg-open"
|
opener = "open" if sys.platform == "darwin" else "xdg-open"
|
||||||
subprocess.call([opener, path])
|
subprocess.call([opener, path])
|
||||||
|
|
||||||
|
|
||||||
bpy.utils.register_class(op)
|
bpy.utils.register_class(op)
|
||||||
|
@ -12,12 +12,12 @@ from . import utilities_bake
|
|||||||
material_prefix = "TT_atlas_"
|
material_prefix = "TT_atlas_"
|
||||||
gamma = 2.2
|
gamma = 2.2
|
||||||
|
|
||||||
|
|
||||||
class op(bpy.types.Operator):
|
class op(bpy.types.Operator):
|
||||||
bl_idname = "uv.textools_texture_preview"
|
bl_idname = "uv.textools_texture_preview"
|
||||||
bl_label = "Preview Texture"
|
bl_label = "Preview Texture"
|
||||||
bl_description = "Preview the current UV image view background image on the selected object."
|
bl_description = "Preview the current UV image view background image on the selected object."
|
||||||
bl_options = {'REGISTER', 'UNDO'}
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
@ -26,25 +26,25 @@ class op(bpy.types.Operator):
|
|||||||
|
|
||||||
if len(settings.sets) == 0:
|
if len(settings.sets) == 0:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Only when we have a background image
|
# Only when we have a background image
|
||||||
for area in bpy.context.screen.areas:
|
for area in bpy.context.screen.areas:
|
||||||
if area.type == 'IMAGE_EDITOR':
|
if area.type == 'IMAGE_EDITOR':
|
||||||
return area.spaces[0].image
|
return area.spaces[0].image
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
print("PREVIEW TEXTURE????")
|
print("PREVIEW TEXTURE????")
|
||||||
preview_texture(self, context)
|
preview_texture(self, context)
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def preview_texture(self, context):
|
def preview_texture(self, context):
|
||||||
|
|
||||||
# Collect all low objects from bake sets
|
# 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
|
# Get view 3D area
|
||||||
view_area = None
|
view_area = None
|
||||||
@ -57,9 +57,8 @@ def preview_texture(self, context):
|
|||||||
# bpy.ops.view3d.localview({'area': view_area})
|
# bpy.ops.view3d.localview({'area': view_area})
|
||||||
# return
|
# return
|
||||||
|
|
||||||
|
|
||||||
# Get background image
|
# Get background image
|
||||||
image = None
|
image = None
|
||||||
for area in bpy.context.screen.areas:
|
for area in bpy.context.screen.areas:
|
||||||
if area.type == 'IMAGE_EDITOR':
|
if area.type == 'IMAGE_EDITOR':
|
||||||
image = area.spaces[0].image
|
image = area.spaces[0].image
|
||||||
@ -71,25 +70,25 @@ def preview_texture(self, context):
|
|||||||
|
|
||||||
bpy.ops.object.mode_set(mode='OBJECT')
|
bpy.ops.object.mode_set(mode='OBJECT')
|
||||||
bpy.ops.object.select_all(action='DESELECT')
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
obj.select_set( state = True, view_layer = None)
|
obj.select_set(state=True, view_layer=None)
|
||||||
bpy.context.view_layer.objects.active = obj
|
bpy.context.view_layer.objects.active = obj
|
||||||
|
|
||||||
for i in range(len(obj.material_slots)):
|
for i in range(len(obj.material_slots)):
|
||||||
bpy.ops.object.material_slot_remove()
|
bpy.ops.object.material_slot_remove()
|
||||||
|
|
||||||
#Create material with image
|
# Create material with image
|
||||||
bpy.ops.object.material_slot_add()
|
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(
|
||||||
obj.display_type = 'TEXTURED'
|
image)
|
||||||
|
obj.display_type = 'TEXTURED'
|
||||||
|
|
||||||
# Re-Select objects
|
# Re-Select objects
|
||||||
bpy.ops.object.select_all(action='DESELECT')
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
for obj in objects:
|
for obj in objects:
|
||||||
obj.select_set( state = True, view_layer = None)
|
obj.select_set(state=True, view_layer=None)
|
||||||
|
|
||||||
if view_area:
|
if view_area:
|
||||||
#Change View mode to TEXTURED
|
# Change View mode to TEXTURED
|
||||||
for space in view_area.spaces:
|
for space in view_area.spaces:
|
||||||
if space.type == 'VIEW_3D':
|
if space.type == 'VIEW_3D':
|
||||||
space.shading.type = 'MATERIAL'
|
space.shading.type = 'MATERIAL'
|
||||||
@ -98,4 +97,5 @@ def preview_texture(self, context):
|
|||||||
# bpy.ops.view3d.localview({'area': view_area})
|
# bpy.ops.view3d.localview({'area': view_area})
|
||||||
# bpy.ops.ui.textools_popup('INVOKE_DEFAULT', message="Object is in isolated view")
|
# bpy.ops.ui.textools_popup('INVOKE_DEFAULT', message="Object is in isolated view")
|
||||||
|
|
||||||
bpy.utils.register_class(op)
|
|
||||||
|
bpy.utils.register_class(op)
|
||||||
|
@ -8,12 +8,13 @@ from math import pi
|
|||||||
from . import utilities_uv
|
from . import utilities_uv
|
||||||
from . import utilities_ui
|
from . import utilities_ui
|
||||||
|
|
||||||
|
|
||||||
class op(bpy.types.Operator):
|
class op(bpy.types.Operator):
|
||||||
bl_idname = "uv.textools_unwrap_edge_peel"
|
bl_idname = "uv.textools_unwrap_edge_peel"
|
||||||
bl_label = "Peel Edge"
|
bl_label = "Peel Edge"
|
||||||
bl_description = "Unwrap pipe along selected edges"
|
bl_description = "Unwrap pipe along selected edges"
|
||||||
bl_options = {'REGISTER', 'UNDO'}
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
|
|
||||||
@ -23,7 +24,7 @@ class op(bpy.types.Operator):
|
|||||||
if bpy.context.active_object.type != 'MESH':
|
if bpy.context.active_object.type != 'MESH':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#Only in Edit mode
|
# Only in Edit mode
|
||||||
if bpy.context.active_object.mode != 'EDIT':
|
if bpy.context.active_object.mode != 'EDIT':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -43,10 +44,10 @@ def unwrap_edges_pipe(self, context):
|
|||||||
bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
|
bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
|
||||||
uv_layers = bm.loops.layers.uv.verify()
|
uv_layers = bm.loops.layers.uv.verify()
|
||||||
|
|
||||||
|
|
||||||
contextViewUV = utilities_ui.GetContextViewUV()
|
contextViewUV = utilities_ui.GetContextViewUV()
|
||||||
if not contextViewUV:
|
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
|
return
|
||||||
|
|
||||||
# selected_initial = [edge for edge in bm.edges if edge.select]
|
# selected_initial = [edge for edge in bm.edges if edge.select]
|
||||||
@ -58,7 +59,7 @@ def unwrap_edges_pipe(self, context):
|
|||||||
selected_edges = [edge for edge in bm.edges if edge.select]
|
selected_edges = [edge for edge in bm.edges if edge.select]
|
||||||
|
|
||||||
if len(selected_edges) == 0:
|
if len(selected_edges) == 0:
|
||||||
self.report({'ERROR_INVALID_INPUT'}, "No edges selected in the view" )
|
self.report({'ERROR_INVALID_INPUT'}, "No edges selected in the view")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Convert linked selection to single UV island
|
# Convert linked selection to single UV island
|
||||||
@ -68,7 +69,7 @@ def unwrap_edges_pipe(self, context):
|
|||||||
selected_faces = [face for face in bm.faces if face.select]
|
selected_faces = [face for face in bm.faces if face.select]
|
||||||
|
|
||||||
if len(selected_faces) == 0:
|
if len(selected_faces) == 0:
|
||||||
self.report({'ERROR_INVALID_INPUT'}, "No faces available" )
|
self.report({'ERROR_INVALID_INPUT'}, "No faces available")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Mark previous selected edges as Seam
|
# Mark previous selected edges as Seam
|
||||||
@ -92,4 +93,5 @@ def unwrap_edges_pipe(self, context):
|
|||||||
# TODO: Restore initial selection
|
# TODO: Restore initial selection
|
||||||
bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE')
|
bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE')
|
||||||
|
|
||||||
bpy.utils.register_class(op)
|
|
||||||
|
bpy.utils.register_class(op)
|
||||||
|
@ -8,59 +8,61 @@ from math import pi
|
|||||||
from . import utilities_uv
|
from . import utilities_uv
|
||||||
from . import utilities_ui
|
from . import utilities_ui
|
||||||
|
|
||||||
|
|
||||||
class op(bpy.types.Operator):
|
class op(bpy.types.Operator):
|
||||||
bl_idname = "uv.textools_uv_crop"
|
bl_idname = "uv.textools_uv_crop"
|
||||||
bl_label = "Crop"
|
bl_label = "Crop"
|
||||||
bl_description = "Crop UV area to selected UV faces"
|
bl_description = "Crop UV area to selected UV faces"
|
||||||
bl_options = {'REGISTER', 'UNDO'}
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
if not bpy.context.active_object:
|
if not bpy.context.active_object:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if bpy.context.active_object.type != 'MESH':
|
if bpy.context.active_object.type != 'MESH':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#Only in Edit mode
|
# Only in Edit mode
|
||||||
if bpy.context.active_object.mode != 'EDIT':
|
if bpy.context.active_object.mode != 'EDIT':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#Only in UV editor mode
|
# Only in UV editor mode
|
||||||
if bpy.context.area.type != 'IMAGE_EDITOR':
|
if bpy.context.area.type != 'IMAGE_EDITOR':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#Requires UV map
|
# Requires UV map
|
||||||
if not bpy.context.object.data.uv_layers:
|
if not bpy.context.object.data.uv_layers:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
crop(self, context)
|
crop(self, context)
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def crop(self, context):
|
def crop(self, context):
|
||||||
bm = bmesh.from_edit_mesh(bpy.context.active_object.data);
|
bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
|
||||||
uv_layers = bm.loops.layers.uv.verify();
|
uv_layers = bm.loops.layers.uv.verify()
|
||||||
|
|
||||||
padding = utilities_ui.get_padding()
|
padding = utilities_ui.get_padding()
|
||||||
|
|
||||||
|
|
||||||
# Scale to fit bounds
|
# Scale to fit bounds
|
||||||
bbox = utilities_uv.getSelectionBBox()
|
bbox = utilities_uv.getSelectionBBox()
|
||||||
scale_u = (1.0-padding) / bbox['width']
|
scale_u = (1.0-padding) / bbox['width']
|
||||||
scale_v = (1.0-padding) / bbox['height']
|
scale_v = (1.0-padding) / bbox['height']
|
||||||
scale = min(scale_u, scale_v)
|
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
|
# Reposition
|
||||||
bbox = utilities_uv.getSelectionBBox()
|
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.ops.transform.translate(value=(delta_position.x, delta_position.y, 0))
|
||||||
|
|
||||||
bpy.utils.register_class(op)
|
|
||||||
|
bpy.utils.register_class(op)
|
||||||
|
128
op_uv_resize.py
128
op_uv_resize.py
@ -18,11 +18,11 @@ utilities_ui.icon_register("op_extend_canvas_BL_active.png")
|
|||||||
utilities_ui.icon_register("op_extend_canvas_BR_active.png")
|
utilities_ui.icon_register("op_extend_canvas_BR_active.png")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def on_dropdown_size_x(self, context):
|
def on_dropdown_size_x(self, context):
|
||||||
self.size_x = int(self.dropdown_size_x)
|
self.size_x = int(self.dropdown_size_x)
|
||||||
# context.area.tag_redraw()
|
# context.area.tag_redraw()
|
||||||
|
|
||||||
|
|
||||||
def on_dropdown_size_y(self, context):
|
def on_dropdown_size_y(self, context):
|
||||||
self.size_y = int(self.dropdown_size_y)
|
self.size_y = int(self.dropdown_size_y)
|
||||||
# context.area.tag_redraw()
|
# context.area.tag_redraw()
|
||||||
@ -34,63 +34,66 @@ class op(bpy.types.Operator):
|
|||||||
bl_description = "Resize or extend the UV area"
|
bl_description = "Resize or extend the UV area"
|
||||||
bl_options = {'REGISTER', 'UNDO'}
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
size_x : bpy.props.IntProperty(
|
size_x: bpy.props.IntProperty(
|
||||||
name = "Width",
|
name="Width",
|
||||||
description="padding size in pixels",
|
description="padding size in pixels",
|
||||||
default = 1024,
|
default=1024,
|
||||||
min = 1,
|
min=1,
|
||||||
max = 8192
|
max=8192
|
||||||
)
|
)
|
||||||
size_y : bpy.props.IntProperty(
|
size_y: bpy.props.IntProperty(
|
||||||
name = "Height",
|
name="Height",
|
||||||
description="padding size in pixels",
|
description="padding size in pixels",
|
||||||
default = 1024,
|
default=1024,
|
||||||
min = 1,
|
min=1,
|
||||||
max = 8192
|
max=8192
|
||||||
)
|
)
|
||||||
dropdown_size_x : bpy.props.EnumProperty(
|
dropdown_size_x: bpy.props.EnumProperty(
|
||||||
items = utilities_ui.size_textures,
|
items=utilities_ui.size_textures,
|
||||||
name = "",
|
name="",
|
||||||
update = on_dropdown_size_x,
|
update=on_dropdown_size_x,
|
||||||
default = '1024'
|
default='1024'
|
||||||
)
|
)
|
||||||
dropdown_size_y : bpy.props.EnumProperty(
|
dropdown_size_y: bpy.props.EnumProperty(
|
||||||
items = utilities_ui.size_textures,
|
items=utilities_ui.size_textures,
|
||||||
name = "",
|
name="",
|
||||||
update = on_dropdown_size_y,
|
update=on_dropdown_size_y,
|
||||||
default = '1024'
|
default='1024'
|
||||||
)
|
)
|
||||||
|
|
||||||
direction : bpy.props.EnumProperty(name='direction', items=(
|
direction: bpy.props.EnumProperty(name='direction', items=(
|
||||||
('TL',' ','Top Left', utilities_ui.icon_get("op_extend_canvas_TL_active"),0),
|
('TL', ' ', 'Top Left', utilities_ui.icon_get(
|
||||||
('BL',' ','Bottom Left', utilities_ui.icon_get("op_extend_canvas_BL_active"),2),
|
"op_extend_canvas_TL_active"), 0),
|
||||||
('TR',' ','Top Right', utilities_ui.icon_get("op_extend_canvas_TR_active"),1),
|
('BL', ' ', 'Bottom Left', utilities_ui.icon_get(
|
||||||
('BR',' ','Bottom Right', utilities_ui.icon_get("op_extend_canvas_BR_active"),3)
|
"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
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
if not bpy.context.active_object:
|
if not bpy.context.active_object:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if bpy.context.active_object.type != 'MESH':
|
if bpy.context.active_object.type != 'MESH':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#Only in Edit mode
|
# Only in Edit mode
|
||||||
if bpy.context.active_object.mode != 'EDIT':
|
if bpy.context.active_object.mode != 'EDIT':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#Only in UV editor mode
|
# Only in UV editor mode
|
||||||
if bpy.context.area.type != 'IMAGE_EDITOR':
|
if bpy.context.area.type != 'IMAGE_EDITOR':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#Requires UV map
|
# Requires UV map
|
||||||
if not bpy.context.object.data.uv_layers:
|
if not bpy.context.object.data.uv_layers:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
print("Invoke resize area")
|
print("Invoke resize area")
|
||||||
self.size_x = bpy.context.scene.texToolsSettings.size[0]
|
self.size_x = bpy.context.scene.texToolsSettings.size[0]
|
||||||
@ -105,8 +108,7 @@ class op(bpy.types.Operator):
|
|||||||
self.dropdown_size_y = item[0]
|
self.dropdown_size_y = item[0]
|
||||||
break
|
break
|
||||||
|
|
||||||
|
return context.window_manager.invoke_props_dialog(self, width=140)
|
||||||
return context.window_manager.invoke_props_dialog(self, width = 140)
|
|
||||||
|
|
||||||
def check(self, context):
|
def check(self, context):
|
||||||
return True
|
return True
|
||||||
@ -115,15 +117,14 @@ class op(bpy.types.Operator):
|
|||||||
# https://b3d.interplanety.org/en/creating-pop-up-panels-with-user-ui-in-blender-add-on/
|
# https://b3d.interplanety.org/en/creating-pop-up-panels-with-user-ui-in-blender-add-on/
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
|
|
||||||
|
|
||||||
layout.separator()
|
layout.separator()
|
||||||
|
|
||||||
# New Size
|
# New Size
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
split = row.split(factor=0.6)
|
split = row.split(factor=0.6)
|
||||||
c = split.column(align=True)
|
c = split.column(align=True)
|
||||||
c.prop(self, "size_x", text="X",expand=True)
|
c.prop(self, "size_x", text="X", expand=True)
|
||||||
c.prop(self, "size_y", text="Y",expand=True)
|
c.prop(self, "size_y", text="Y", expand=True)
|
||||||
|
|
||||||
c = split.column(align=True)
|
c = split.column(align=True)
|
||||||
c.prop(self, "dropdown_size_x", text="")
|
c.prop(self, "dropdown_size_x", text="")
|
||||||
@ -133,10 +134,11 @@ class op(bpy.types.Operator):
|
|||||||
col = layout.column(align=True)
|
col = layout.column(align=True)
|
||||||
col.label(text="Direction")
|
col.label(text="Direction")
|
||||||
row = col.row(align=True)
|
row = col.row(align=True)
|
||||||
row.prop(self,'direction', expand=True)
|
row.prop(self, 'direction', expand=True)
|
||||||
|
|
||||||
# Summary
|
# 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]:
|
if bpy.context.scene.texToolsSettings.size[0] == bpy.context.scene.texToolsSettings.size[1]:
|
||||||
size_A = "{}²".format(bpy.context.scene.texToolsSettings.size[0])
|
size_A = "{}²".format(bpy.context.scene.texToolsSettings.size[0])
|
||||||
size_B = "{} x {}".format(self.size_x, self.size_y)
|
size_B = "{} x {}".format(self.size_x, self.size_y)
|
||||||
@ -147,21 +149,19 @@ class op(bpy.types.Operator):
|
|||||||
size_A, size_B
|
size_A, size_B
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
||||||
layout.separator()
|
layout.separator()
|
||||||
|
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
|
|
||||||
#Store selection
|
# Store selection
|
||||||
utilities_uv.selection_store()
|
utilities_uv.selection_store()
|
||||||
|
|
||||||
# Get start and end size
|
# Get start and end size
|
||||||
size_A = Vector([
|
size_A = Vector([
|
||||||
bpy.context.scene.texToolsSettings.size[0],
|
bpy.context.scene.texToolsSettings.size[0],
|
||||||
bpy.context.scene.texToolsSettings.size[1]
|
bpy.context.scene.texToolsSettings.size[1]
|
||||||
])
|
])
|
||||||
size_B = Vector([
|
size_B = Vector([
|
||||||
self.size_x,
|
self.size_x,
|
||||||
self.size_y
|
self.size_y
|
||||||
])
|
])
|
||||||
@ -170,7 +170,7 @@ class op(bpy.types.Operator):
|
|||||||
self,
|
self,
|
||||||
context,
|
context,
|
||||||
self.direction,
|
self.direction,
|
||||||
size_A,
|
size_A,
|
||||||
size_B
|
size_B
|
||||||
)
|
)
|
||||||
resize_image(
|
resize_image(
|
||||||
@ -183,25 +183,24 @@ class op(bpy.types.Operator):
|
|||||||
bpy.context.scene.texToolsSettings.size[0] = self.size_x
|
bpy.context.scene.texToolsSettings.size[0] = self.size_x
|
||||||
bpy.context.scene.texToolsSettings.size[1] = self.size_y
|
bpy.context.scene.texToolsSettings.size[1] = self.size_y
|
||||||
|
|
||||||
#Restore selection
|
# Restore selection
|
||||||
utilities_uv.selection_restore()
|
utilities_uv.selection_restore()
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def resize_uv(self, context, mode, size_A, size_B):
|
def resize_uv(self, context, mode, size_A, size_B):
|
||||||
|
|
||||||
# Set pivot
|
# Set pivot
|
||||||
bpy.context.tool_settings.transform_pivot_point = 'CURSOR'
|
bpy.context.tool_settings.transform_pivot_point = 'CURSOR'
|
||||||
if mode == 'TL':
|
if mode == 'TL':
|
||||||
bpy.ops.uv.cursor_set(location=Vector([0,1]))
|
bpy.ops.uv.cursor_set(location=Vector([0, 1]))
|
||||||
elif mode == 'TR':
|
elif mode == 'TR':
|
||||||
bpy.ops.uv.cursor_set(location=Vector([1,1]))
|
bpy.ops.uv.cursor_set(location=Vector([1, 1]))
|
||||||
elif mode == 'BL':
|
elif mode == 'BL':
|
||||||
bpy.ops.uv.cursor_set(location=Vector([0,0]))
|
bpy.ops.uv.cursor_set(location=Vector([0, 0]))
|
||||||
elif mode == 'BR':
|
elif mode == 'BR':
|
||||||
bpy.ops.uv.cursor_set(location=Vector([1,0]))
|
bpy.ops.uv.cursor_set(location=Vector([1, 0]))
|
||||||
|
|
||||||
# Select all UV faces
|
# Select all UV faces
|
||||||
bpy.ops.uv.select_all(action='SELECT')
|
bpy.ops.uv.select_all(action='SELECT')
|
||||||
@ -209,24 +208,25 @@ def resize_uv(self, context, mode, size_A, size_B):
|
|||||||
# Resize
|
# Resize
|
||||||
scale_x = size_A.x / size_B.x
|
scale_x = size_A.x / size_B.x
|
||||||
scale_y = size_A.y / size_B.y
|
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):
|
def resize_image(context, mode, size_A, size_B):
|
||||||
print("resize image {}".format( context.area.spaces ))
|
print("resize image {}".format(context.area.spaces))
|
||||||
|
|
||||||
# Notes: https://blender.stackexchange.com/questions/31514/active-image-of-uv-image-editor
|
# 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
|
# 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 != None:
|
||||||
if context.area.spaces.active.image != None:
|
if context.area.spaces.active.image != None:
|
||||||
image = context.area.spaces.active.image
|
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:
|
if name_texture in image.name or image == image_obj:
|
||||||
# Resize Image UV editor background image
|
# 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:
|
else:
|
||||||
# No Image assigned
|
# No Image assigned
|
||||||
@ -234,20 +234,21 @@ def resize_image(context, mode, size_A, size_B):
|
|||||||
# Get background color from theme + 1.25x brighter
|
# Get background color from theme + 1.25x brighter
|
||||||
theme = bpy.context.preferences.themes[0]
|
theme = bpy.context.preferences.themes[0]
|
||||||
color = theme.image_editor.space.back.copy()
|
color = theme.image_editor.space.back.copy()
|
||||||
color.r*= 1.15
|
color.r *= 1.15
|
||||||
color.g*= 1.15
|
color.g *= 1.15
|
||||||
color.b*= 1.15
|
color.b *= 1.15
|
||||||
|
|
||||||
image = None
|
image = None
|
||||||
if name_texture in bpy.data.images:
|
if name_texture in bpy.data.images:
|
||||||
# TexTools Image already exists
|
# TexTools Image already exists
|
||||||
image = bpy.data.images[name_texture]
|
image = bpy.data.images[name_texture]
|
||||||
image.scale( int(size_B.x), int(size_B.y) )
|
image.scale(int(size_B.x), int(size_B.y))
|
||||||
image.generated_width = int(size_B.x)
|
image.generated_width = int(size_B.x)
|
||||||
image.generated_height = int(size_B.y)
|
image.generated_height = int(size_B.y)
|
||||||
else:
|
else:
|
||||||
# Create new image
|
# 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_color = (color.r, color.g, color.b, 1.0)
|
||||||
image.generated_type = 'BLANK'
|
image.generated_type = 'BLANK'
|
||||||
image.generated_width = int(size_B.x)
|
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()
|
utilities_texel.checker_images_cleanup()
|
||||||
|
|
||||||
|
|
||||||
|
bpy.utils.register_class(op)
|
||||||
bpy.utils.register_class(op)
|
|
||||||
|
@ -12,27 +12,26 @@ from . import utilities_color
|
|||||||
# from . import op_bake
|
# from . import op_bake
|
||||||
|
|
||||||
|
|
||||||
keywords_low = ['lowpoly','low','lowp','lp','lo','l']
|
keywords_low = ['lowpoly', 'low', 'lowp', 'lp', 'lo', 'l']
|
||||||
keywords_high = ['highpoly','high','highp','hp','hi','h']
|
keywords_high = ['highpoly', 'high', 'highp', 'hp', 'hi', 'h']
|
||||||
keywords_cage = ['cage','c']
|
keywords_cage = ['cage', 'c']
|
||||||
keywords_float = ['floater','float','f']
|
keywords_float = ['floater', 'float', 'f']
|
||||||
|
|
||||||
split_chars = [' ','_','.','-']
|
|
||||||
|
|
||||||
|
split_chars = [' ', '_', '.', '-']
|
||||||
|
|
||||||
|
|
||||||
class BakeMode:
|
class BakeMode:
|
||||||
material = "" #Material name from external blend file
|
material = "" # Material name from external blend file
|
||||||
type = 'EMIT'
|
type = 'EMIT'
|
||||||
normal_space = 'TANGENT'
|
normal_space = 'TANGENT'
|
||||||
setVColor = None #Set Vertex color method
|
setVColor = None # Set Vertex color method
|
||||||
color = (0.23, 0.23, 0.23, 1) #Background color
|
color = (0.23, 0.23, 0.23, 1) # Background color
|
||||||
engine = 'CYCLES' #render engine, by default CYCLES
|
engine = 'CYCLES' # render engine, by default CYCLES
|
||||||
composite = None #use composite scene to process end result
|
composite = None # use composite scene to process end result
|
||||||
use_project = False #Bake projected?
|
use_project = False # Bake projected?
|
||||||
params = [] #UI Parameters from scene settings
|
params = [] # UI Parameters from scene settings
|
||||||
|
|
||||||
def __init__(self, material="", type='EMIT', normal_space='TANGENT', setVColor=None, color= (0.23, 0.23, 0.23, 1), engine='CYCLES', params = [], composite=None, use_project=False):
|
def __init__(self, material="", type='EMIT', normal_space='TANGENT', setVColor=None, color=(0.23, 0.23, 0.23, 1), engine='CYCLES', params=[], composite=None, use_project=False):
|
||||||
self.material = material
|
self.material = material
|
||||||
self.type = type
|
self.type = type
|
||||||
self.normal_space = normal_space
|
self.normal_space = normal_space
|
||||||
@ -44,7 +43,6 @@ class BakeMode:
|
|||||||
self.use_project = use_project
|
self.use_project = use_project
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def on_select_bake_mode(mode):
|
def on_select_bake_mode(mode):
|
||||||
print("Mode changed {}".format(mode))
|
print("Mode changed {}".format(mode))
|
||||||
|
|
||||||
@ -81,8 +79,6 @@ def store_bake_settings():
|
|||||||
|
|
||||||
settings.bake_objects_hide_render = []
|
settings.bake_objects_hide_render = []
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# for obj in bpy.context.view_layer.objects:
|
# for obj in bpy.context.view_layer.objects:
|
||||||
# if obj.hide_render == False and obj not in objects_sets:
|
# if obj.hide_render == False and obj not in objects_sets:
|
||||||
# Check if layer is active:
|
# Check if layer is active:
|
||||||
@ -96,7 +92,6 @@ def store_bake_settings():
|
|||||||
# obj.cycles_visibility.shadow = False
|
# obj.cycles_visibility.shadow = False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def restore_bake_settings():
|
def restore_bake_settings():
|
||||||
# Render Settings
|
# Render Settings
|
||||||
if settings.bake_render_engine != '':
|
if settings.bake_render_engine != '':
|
||||||
@ -111,59 +106,59 @@ def restore_bake_settings():
|
|||||||
# obj.cycles_visibility.shadow = True
|
# obj.cycles_visibility.shadow = True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
stored_materials = {}
|
stored_materials = {}
|
||||||
stored_material_faces = {}
|
stored_material_faces = {}
|
||||||
|
|
||||||
|
|
||||||
def store_materials_clear():
|
def store_materials_clear():
|
||||||
stored_materials.clear()
|
stored_materials.clear()
|
||||||
stored_material_faces.clear()
|
stored_material_faces.clear()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def store_materials(obj):
|
def store_materials(obj):
|
||||||
stored_materials[obj] = []
|
stored_materials[obj] = []
|
||||||
stored_material_faces[obj] = []
|
stored_material_faces[obj] = []
|
||||||
|
|
||||||
# Enter edit mode
|
# Enter edit mode
|
||||||
bpy.ops.object.select_all(action='DESELECT')
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
obj.select_set( state = True, view_layer = None)
|
obj.select_set(state=True, view_layer=None)
|
||||||
bpy.context.view_layer.objects.active = obj
|
bpy.context.view_layer.objects.active = obj
|
||||||
|
|
||||||
bpy.ops.object.mode_set(mode='EDIT')
|
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 each slot backup the material
|
||||||
for s in range(len(obj.material_slots)):
|
for s in range(len(obj.material_slots)):
|
||||||
slot = obj.material_slots[s]
|
slot = obj.material_slots[s]
|
||||||
|
|
||||||
stored_materials[obj].append(slot.material)
|
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]) ))
|
# print("Faces: {}x".format( len(stored_material_faces[obj][-1]) ))
|
||||||
|
|
||||||
if slot and slot.material:
|
if slot and slot.material:
|
||||||
slot.material.name = "backup_"+slot.material.name
|
slot.material.name = "backup_"+slot.material.name
|
||||||
print("- Store {} = {}".format(obj.name,slot.material.name))
|
print("- Store {} = {}".format(obj.name, slot.material.name))
|
||||||
|
|
||||||
# Back to object mode
|
# Back to object mode
|
||||||
bpy.ops.object.mode_set(mode='OBJECT')
|
bpy.ops.object.mode_set(mode='OBJECT')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def restore_materials():
|
def restore_materials():
|
||||||
for obj in stored_materials:
|
for obj in stored_materials:
|
||||||
# Enter edit mode
|
# Enter edit mode
|
||||||
bpy.context.view_layer.objects.active = obj
|
bpy.context.view_layer.objects.active = obj
|
||||||
bpy.ops.object.mode_set(mode='EDIT')
|
bpy.ops.object.mode_set(mode='EDIT')
|
||||||
bm = bmesh.from_edit_mesh(obj.data);
|
bm = bmesh.from_edit_mesh(obj.data)
|
||||||
|
|
||||||
# Restore slots
|
# Restore slots
|
||||||
for index in range(len(stored_materials[obj])):
|
for index in range(len(stored_materials[obj])):
|
||||||
material = stored_materials[obj][index]
|
material = stored_materials[obj][index]
|
||||||
faces = stored_material_faces[obj][index]
|
faces = stored_material_faces[obj][index]
|
||||||
|
|
||||||
if material:
|
if material:
|
||||||
material.name = material.name.replace("backup_","")
|
material.name = material.name.replace("backup_", "")
|
||||||
obj.material_slots[index].material = material
|
obj.material_slots[index].material = material
|
||||||
|
|
||||||
# Face material indexies
|
# Face material indexies
|
||||||
@ -180,12 +175,11 @@ def restore_materials():
|
|||||||
bpy.ops.object.material_slot_remove()
|
bpy.ops.object.material_slot_remove()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_set_name_base(obj):
|
def get_set_name_base(obj):
|
||||||
|
|
||||||
def remove_digits(name):
|
def remove_digits(name):
|
||||||
# Remove blender naming digits, e.g. cube.001, cube.002,...
|
# Remove blender naming digits, e.g. cube.001, cube.002,...
|
||||||
if len(name)>= 4 and name[-4] == '.' and name[-3].isdigit() and name[-2].isdigit() and name[-1].isdigit():
|
if len(name) >= 4 and name[-4] == '.' and name[-3].isdigit() and name[-2].isdigit() and name[-1].isdigit():
|
||||||
return name[:-4]
|
return name[:-4]
|
||||||
return name
|
return name
|
||||||
|
|
||||||
@ -202,7 +196,6 @@ def get_set_name_base(obj):
|
|||||||
return remove_digits(obj.name).lower()
|
return remove_digits(obj.name).lower()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_set_name(obj):
|
def get_set_name(obj):
|
||||||
# Get Basic name
|
# Get Basic name
|
||||||
name = get_set_name_base(obj)
|
name = get_set_name_base(obj)
|
||||||
@ -210,7 +203,7 @@ def get_set_name(obj):
|
|||||||
# Split by ' ','_','.' etc.
|
# Split by ' ','_','.' etc.
|
||||||
split = name.lower()
|
split = name.lower()
|
||||||
for char in split_chars:
|
for char in split_chars:
|
||||||
split = split.replace(char,' ')
|
split = split.replace(char, ' ')
|
||||||
strings = split.split(' ')
|
strings = split.split(' ')
|
||||||
|
|
||||||
# Remove all keys from name
|
# Remove all keys from name
|
||||||
@ -231,7 +224,6 @@ def get_set_name(obj):
|
|||||||
return "_".join(new_strings)
|
return "_".join(new_strings)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_object_type(obj):
|
def get_object_type(obj):
|
||||||
|
|
||||||
name = get_set_name_base(obj)
|
name = get_set_name_base(obj)
|
||||||
@ -239,11 +231,11 @@ def get_object_type(obj):
|
|||||||
# Detect by name pattern
|
# Detect by name pattern
|
||||||
split = name.lower()
|
split = name.lower()
|
||||||
for char in split_chars:
|
for char in split_chars:
|
||||||
split = split.replace(char,' ')
|
split = split.replace(char, ' ')
|
||||||
strings = split.split(' ')
|
strings = split.split(' ')
|
||||||
|
|
||||||
# Detect float, more rare than low
|
# Detect float, more rare than low
|
||||||
for string in strings:
|
for string in strings:
|
||||||
for key in keywords_float:
|
for key in keywords_float:
|
||||||
if key == string:
|
if key == string:
|
||||||
return 'float'
|
return 'float'
|
||||||
@ -257,33 +249,28 @@ def get_object_type(obj):
|
|||||||
elif modifier.type == 'BEVEL':
|
elif modifier.type == 'BEVEL':
|
||||||
return 'high'
|
return 'high'
|
||||||
|
|
||||||
|
|
||||||
# Detect High first, more rare
|
# Detect High first, more rare
|
||||||
for string in strings:
|
for string in strings:
|
||||||
for key in keywords_high:
|
for key in keywords_high:
|
||||||
if key == string:
|
if key == string:
|
||||||
return 'high'
|
return 'high'
|
||||||
|
|
||||||
# Detect cage, more rare than low
|
# Detect cage, more rare than low
|
||||||
for string in strings:
|
for string in strings:
|
||||||
for key in keywords_cage:
|
for key in keywords_cage:
|
||||||
if key == string:
|
if key == string:
|
||||||
return 'cage'
|
return 'cage'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Detect low
|
# Detect low
|
||||||
for string in strings:
|
for string in strings:
|
||||||
for key in keywords_low:
|
for key in keywords_low:
|
||||||
if key == string:
|
if key == string:
|
||||||
return 'low'
|
return 'low'
|
||||||
|
|
||||||
|
|
||||||
# if nothing was detected, assume its low
|
# if nothing was detected, assume its low
|
||||||
return 'low'
|
return 'low'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_baked_images(sets):
|
def get_baked_images(sets):
|
||||||
images = []
|
images = []
|
||||||
for set in sets:
|
for set in sets:
|
||||||
@ -295,19 +282,18 @@ def get_baked_images(sets):
|
|||||||
return images
|
return images
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_bake_sets():
|
def get_bake_sets():
|
||||||
filtered = {}
|
filtered = {}
|
||||||
for obj in bpy.context.selected_objects:
|
for obj in bpy.context.selected_objects:
|
||||||
if obj.type == 'MESH':
|
if obj.type == 'MESH':
|
||||||
filtered[obj] = get_object_type(obj)
|
filtered[obj] = get_object_type(obj)
|
||||||
|
|
||||||
groups = []
|
groups = []
|
||||||
# Group by names
|
# Group by names
|
||||||
for obj in filtered:
|
for obj in filtered:
|
||||||
name = get_set_name(obj)
|
name = get_set_name(obj)
|
||||||
|
|
||||||
if len(groups)==0:
|
if len(groups) == 0:
|
||||||
groups.append([obj])
|
groups.append([obj])
|
||||||
else:
|
else:
|
||||||
isFound = False
|
isFound = False
|
||||||
@ -330,10 +316,9 @@ def get_bake_sets():
|
|||||||
if key == get_set_name(group[0]):
|
if key == get_set_name(group[0]):
|
||||||
sorted_groups.append(group)
|
sorted_groups.append(group)
|
||||||
break
|
break
|
||||||
|
|
||||||
groups = sorted_groups
|
|
||||||
# print("Keys: "+", ".join(keys))
|
|
||||||
|
|
||||||
|
groups = sorted_groups
|
||||||
|
# print("Keys: "+", ".join(keys))
|
||||||
|
|
||||||
bake_sets = []
|
bake_sets = []
|
||||||
for group in groups:
|
for group in groups:
|
||||||
@ -351,19 +336,17 @@ def get_bake_sets():
|
|||||||
elif filtered[obj] == 'float':
|
elif filtered[obj] == 'float':
|
||||||
float.append(obj)
|
float.append(obj)
|
||||||
|
|
||||||
|
|
||||||
name = get_set_name(group[0])
|
name = get_set_name(group[0])
|
||||||
bake_sets.append(BakeSet(name, low, cage, high, float))
|
bake_sets.append(BakeSet(name, low, cage, high, float))
|
||||||
|
|
||||||
return bake_sets
|
return bake_sets
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class BakeSet:
|
class BakeSet:
|
||||||
objects_low = [] #low poly geometry
|
objects_low = [] # low poly geometry
|
||||||
objects_cage = [] #Cage low poly geometry
|
objects_cage = [] # Cage low poly geometry
|
||||||
objects_high = [] #High poly geometry
|
objects_high = [] # High poly geometry
|
||||||
objects_float = [] #Floating geometry
|
objects_float = [] # Floating geometry
|
||||||
name = ""
|
name = ""
|
||||||
|
|
||||||
has_issues = False
|
has_issues = False
|
||||||
@ -390,14 +373,12 @@ class BakeSet:
|
|||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def setup_vertex_color_selection(obj):
|
def setup_vertex_color_selection(obj):
|
||||||
bpy.ops.object.mode_set(mode='OBJECT')
|
bpy.ops.object.mode_set(mode='OBJECT')
|
||||||
|
|
||||||
bpy.ops.object.select_all(action='DESELECT')
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
obj.select_set( state = True, view_layer = None)
|
obj.select_set(state=True, view_layer=None)
|
||||||
bpy.context.view_layer.objects.active = obj
|
bpy.context.view_layer.objects.active = obj
|
||||||
|
|
||||||
|
|
||||||
bpy.ops.object.mode_set(mode='VERTEX_PAINT')
|
bpy.ops.object.mode_set(mode='VERTEX_PAINT')
|
||||||
|
|
||||||
@ -415,26 +396,24 @@ def setup_vertex_color_selection(obj):
|
|||||||
bpy.ops.object.mode_set(mode='OBJECT')
|
bpy.ops.object.mode_set(mode='OBJECT')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def setup_vertex_color_dirty(obj):
|
def setup_vertex_color_dirty(obj):
|
||||||
|
|
||||||
print("setup_vertex_color_dirty {}".format(obj.name))
|
print("setup_vertex_color_dirty {}".format(obj.name))
|
||||||
|
|
||||||
bpy.ops.object.select_all(action='DESELECT')
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
obj.select_set( state = True, view_layer = None)
|
obj.select_set(state=True, view_layer=None)
|
||||||
bpy.context.view_layer.objects.active = obj
|
bpy.context.view_layer.objects.active = obj
|
||||||
bpy.ops.object.mode_set(mode='EDIT')
|
bpy.ops.object.mode_set(mode='EDIT')
|
||||||
|
|
||||||
# Fill white then,
|
# Fill white then,
|
||||||
bm = bmesh.from_edit_mesh(obj.data)
|
bm = bmesh.from_edit_mesh(obj.data)
|
||||||
colorLayer = bm.loops.layers.color.verify()
|
colorLayer = bm.loops.layers.color.verify()
|
||||||
|
|
||||||
|
color = utilities_color.safe_color((1, 1, 1))
|
||||||
color = utilities_color.safe_color( (1, 1, 1) )
|
|
||||||
|
|
||||||
for face in bm.faces:
|
for face in bm.faces:
|
||||||
for loop in face.loops:
|
for loop in face.loops:
|
||||||
loop[colorLayer] = color
|
loop[colorLayer] = color
|
||||||
obj.data.update()
|
obj.data.update()
|
||||||
|
|
||||||
# Back to object mode
|
# Back to object mode
|
||||||
@ -443,13 +422,11 @@ def setup_vertex_color_dirty(obj):
|
|||||||
bpy.ops.paint.vertex_color_dirt()
|
bpy.ops.paint.vertex_color_dirt()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def setup_vertex_color_id_material(obj):
|
def setup_vertex_color_id_material(obj):
|
||||||
bpy.ops.object.select_all(action='DESELECT')
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
obj.select_set( state = True, view_layer = None)
|
obj.select_set(state=True, view_layer=None)
|
||||||
bpy.context.view_layer.objects.active = obj
|
bpy.context.view_layer.objects.active = obj
|
||||||
|
|
||||||
|
|
||||||
bpy.ops.object.mode_set(mode='EDIT')
|
bpy.ops.object.mode_set(mode='EDIT')
|
||||||
bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE')
|
bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE')
|
||||||
|
|
||||||
@ -482,10 +459,9 @@ def setup_vertex_color_id_material(obj):
|
|||||||
bpy.ops.object.mode_set(mode='OBJECT')
|
bpy.ops.object.mode_set(mode='OBJECT')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def setup_vertex_color_id_element(obj):
|
def setup_vertex_color_id_element(obj):
|
||||||
bpy.ops.object.select_all(action='DESELECT')
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
obj.select_set( state = True, view_layer = None)
|
obj.select_set(state=True, view_layer=None)
|
||||||
bpy.context.view_layer.objects.active = obj
|
bpy.context.view_layer.objects.active = obj
|
||||||
bpy.ops.object.mode_set(mode='EDIT')
|
bpy.ops.object.mode_set(mode='EDIT')
|
||||||
|
|
||||||
@ -510,9 +486,9 @@ def setup_vertex_color_id_element(obj):
|
|||||||
groups.append(linked)
|
groups.append(linked)
|
||||||
|
|
||||||
# Color each group
|
# Color each group
|
||||||
for i in range(0,len(groups)):
|
for i in range(0, len(groups)):
|
||||||
color = utilities_color.get_color_id(i, len(groups))
|
color = utilities_color.get_color_id(i, len(groups))
|
||||||
color = utilities_color.safe_color( color )
|
color = utilities_color.safe_color(color)
|
||||||
for face in groups[i]:
|
for face in groups[i]:
|
||||||
for loop in face.loops:
|
for loop in face.loops:
|
||||||
loop[colorLayer] = color
|
loop[colorLayer] = color
|
||||||
@ -535,7 +511,6 @@ def get_image_material(image):
|
|||||||
else:
|
else:
|
||||||
material = bpy.data.materials.new(image.name)
|
material = bpy.data.materials.new(image.name)
|
||||||
|
|
||||||
|
|
||||||
# Cyles Material
|
# Cyles Material
|
||||||
if bpy.context.scene.render.engine == 'CYCLES' or bpy.context.scene.render.engine == 'BLENDER_EEVEE':
|
if bpy.context.scene.render.engine == 'CYCLES' or bpy.context.scene.render.engine == 'BLENDER_EEVEE':
|
||||||
material.use_nodes = True
|
material.use_nodes = True
|
||||||
@ -551,17 +526,17 @@ def get_image_material(image):
|
|||||||
node_image.image = image
|
node_image.image = image
|
||||||
material.node_tree.nodes.active = node_image
|
material.node_tree.nodes.active = node_image
|
||||||
|
|
||||||
#Base Diffuse BSDF
|
# Base Diffuse BSDF
|
||||||
node_diffuse = material.node_tree.nodes['Principled BSDF']
|
node_diffuse = material.node_tree.nodes['Principled BSDF']
|
||||||
|
|
||||||
|
|
||||||
if "_normal_" in image.name:
|
if "_normal_" in image.name:
|
||||||
# Add Normal Map Nodes
|
# Add Normal Map Nodes
|
||||||
node_normal_map = None
|
node_normal_map = None
|
||||||
if "normal_map" in material.node_tree.nodes:
|
if "normal_map" in material.node_tree.nodes:
|
||||||
node_normal_map = material.node_tree.nodes["normal_map"]
|
node_normal_map = material.node_tree.nodes["normal_map"]
|
||||||
else:
|
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"
|
node_normal_map.name = "normal_map"
|
||||||
|
|
||||||
# Tangent or World space
|
# Tangent or World space
|
||||||
@ -571,10 +546,12 @@ def get_image_material(image):
|
|||||||
node_normal_map.space = 'WORLD'
|
node_normal_map.space = 'WORLD'
|
||||||
|
|
||||||
# image to normal_map link
|
# 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
|
# 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_normal_map.location = node_diffuse.location - Vector((200, 0))
|
||||||
node_image.location = node_normal_map.location - Vector((200, 0))
|
node_image.location = node_normal_map.location - Vector((200, 0))
|
||||||
@ -582,15 +559,16 @@ def get_image_material(image):
|
|||||||
else:
|
else:
|
||||||
# Other images display as Color
|
# Other images display as Color
|
||||||
# dump(node_image.color_mapping.bl_rna.property_tags)
|
# dump(node_image.color_mapping.bl_rna.property_tags)
|
||||||
|
|
||||||
# image node to diffuse node link
|
# 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
|
return material
|
||||||
|
|
||||||
elif bpy.context.scene.render.engine == 'BLENDER_EEVEE':
|
elif bpy.context.scene.render.engine == 'BLENDER_EEVEE':
|
||||||
material.use_nodes = True
|
material.use_nodes = True
|
||||||
|
|
||||||
texture = None
|
texture = None
|
||||||
if image.name in bpy.data.textures:
|
if image.name in bpy.data.textures:
|
||||||
texture = bpy.data.textures[image.name]
|
texture = bpy.data.textures[image.name]
|
||||||
@ -600,6 +578,6 @@ def get_image_material(image):
|
|||||||
texture.image = image
|
texture.image = image
|
||||||
slot = material.texture_slot.add()
|
slot = material.texture_slot.add()
|
||||||
slot.texture = texture
|
slot.texture = texture
|
||||||
slot.mapping = 'FLAT'
|
slot.mapping = 'FLAT'
|
||||||
|
|
||||||
# return material
|
# return material
|
||||||
|
@ -43,28 +43,26 @@ def assign_color(index):
|
|||||||
material = get_material(index)
|
material = get_material(index)
|
||||||
if material:
|
if material:
|
||||||
# material.use_nodes = False
|
# material.use_nodes = False
|
||||||
|
|
||||||
rgb = get_color(index)
|
rgb = get_color(index)
|
||||||
rgba = (rgb[0], rgb[1], rgb[2], 1)
|
rgba = (rgb[0], rgb[1], rgb[2], 1)
|
||||||
|
|
||||||
if material.use_nodes and bpy.context.scene.render.engine == 'CYCLES' or material.use_nodes and bpy.context.scene.render.engine == 'BLENDER_EEVEE' :
|
if material.use_nodes and bpy.context.scene.render.engine == 'CYCLES' or material.use_nodes and bpy.context.scene.render.engine == 'BLENDER_EEVEE':
|
||||||
# Cycles material (Preferred for baking)
|
# Cycles material (Preferred for baking)
|
||||||
material.node_tree.nodes["Principled BSDF"].inputs[0].default_value = rgba
|
material.node_tree.nodes["Principled BSDF"].inputs[0].default_value = rgba
|
||||||
material.diffuse_color = rgba
|
material.diffuse_color = rgba
|
||||||
|
|
||||||
|
|
||||||
elif not material.use_nodes and bpy.context.scene.render.engine == 'BLENDER_EEVEE':
|
elif not material.use_nodes and bpy.context.scene.render.engine == 'BLENDER_EEVEE':
|
||||||
# Legacy render engine, not suited for baking
|
# Legacy render engine, not suited for baking
|
||||||
material.diffuse_color = rgba
|
material.diffuse_color = rgba
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_material(index):
|
def get_material(index):
|
||||||
name = get_name(index)
|
name = get_name(index)
|
||||||
|
|
||||||
# Material already exists?
|
# Material already exists?
|
||||||
if name in bpy.data.materials:
|
if name in bpy.data.materials:
|
||||||
material = bpy.data.materials[name];
|
material = bpy.data.materials[name]
|
||||||
|
|
||||||
# Check for incorrect matreials for current render engine
|
# Check for incorrect matreials for current render engine
|
||||||
if not material:
|
if not material:
|
||||||
@ -77,7 +75,7 @@ def get_material(index):
|
|||||||
replace_material(index)
|
replace_material(index)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return material;
|
return material
|
||||||
|
|
||||||
print("Could nt find {} , create a new one??".format(name))
|
print("Could nt find {} , create a new one??".format(name))
|
||||||
|
|
||||||
@ -86,7 +84,6 @@ def get_material(index):
|
|||||||
return material
|
return material
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Replaace an existing material with a new one
|
# Replaace an existing material with a new one
|
||||||
# This is sometimes necessary after switching the render engine
|
# This is sometimes necessary after switching the render engine
|
||||||
def replace_material(index):
|
def replace_material(index):
|
||||||
@ -96,11 +93,11 @@ def replace_material(index):
|
|||||||
|
|
||||||
# Check if material exists
|
# Check if material exists
|
||||||
if name in bpy.data.materials:
|
if name in bpy.data.materials:
|
||||||
material = bpy.data.materials[name];
|
material = bpy.data.materials[name]
|
||||||
|
|
||||||
# Collect material slots we have to re-assign
|
# Collect material slots we have to re-assign
|
||||||
slots = []
|
slots = []
|
||||||
for obj in bpy.context.view_layer.objects:
|
for obj in bpy.context.view_layer.objects:
|
||||||
for slot in obj.material_slots:
|
for slot in obj.material_slots:
|
||||||
if slot.material == material:
|
if slot.material == material:
|
||||||
slots.append(slot)
|
slots.append(slot)
|
||||||
@ -108,12 +105,11 @@ def replace_material(index):
|
|||||||
# Get new material
|
# Get new material
|
||||||
material.user_clear()
|
material.user_clear()
|
||||||
bpy.data.materials.remove(material)
|
bpy.data.materials.remove(material)
|
||||||
|
|
||||||
# Re-assign new material to all previous slots
|
# Re-assign new material to all previous slots
|
||||||
material = create_material(index)
|
material = create_material(index)
|
||||||
for slot in slots:
|
for slot in slots:
|
||||||
slot.material = material;
|
slot.material = material
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def create_material(index):
|
def create_material(index):
|
||||||
@ -125,17 +121,15 @@ def create_material(index):
|
|||||||
|
|
||||||
if bpy.context.scene.render.engine == 'CYCLES':
|
if bpy.context.scene.render.engine == 'CYCLES':
|
||||||
# Cycles: prefer nodes as it simplifies baking
|
# Cycles: prefer nodes as it simplifies baking
|
||||||
material.use_nodes = True
|
material.use_nodes = True
|
||||||
|
|
||||||
return material
|
return material
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_name(index):
|
def get_name(index):
|
||||||
return (material_prefix+"{:02d}").format(index)
|
return (material_prefix+"{:02d}").format(index)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_color(index):
|
def get_color(index):
|
||||||
if index < bpy.context.scene.texToolsSettings.color_ID_count:
|
if index < bpy.context.scene.texToolsSettings.color_ID_count:
|
||||||
return getattr(bpy.context.scene.texToolsSettings, "color_ID_color_{}".format(index))
|
return getattr(bpy.context.scene.texToolsSettings, "color_ID_color_{}".format(index))
|
||||||
@ -144,16 +138,15 @@ def get_color(index):
|
|||||||
return (0, 0, 0)
|
return (0, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def set_color(index, color):
|
def set_color(index, color):
|
||||||
if index < bpy.context.scene.texToolsSettings.color_ID_count:
|
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):
|
def validate_face_colors(obj):
|
||||||
# Validate face colors and material slots
|
# 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
|
count = bpy.context.scene.texToolsSettings.color_ID_count
|
||||||
|
|
||||||
# Verify enough material slots
|
# Verify enough material slots
|
||||||
@ -165,12 +158,11 @@ def validate_face_colors(obj):
|
|||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
# TODO: Check face.material_index
|
# TODO: Check face.material_index
|
||||||
bpy.ops.object.mode_set(mode='EDIT')
|
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:
|
for face in bm.faces:
|
||||||
face.material_index%= count
|
face.material_index %= count
|
||||||
obj.data.update()
|
obj.data.update()
|
||||||
|
|
||||||
# Remove material slots that are not used
|
# Remove material slots that are not used
|
||||||
@ -179,19 +171,16 @@ def validate_face_colors(obj):
|
|||||||
for i in range(len(obj.material_slots) - count):
|
for i in range(len(obj.material_slots) - count):
|
||||||
if len(obj.material_slots) > count:
|
if len(obj.material_slots) > count:
|
||||||
# Remove last
|
# 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()
|
bpy.ops.object.material_slot_remove()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Restore previous mode
|
# Restore previous mode
|
||||||
bpy.ops.object.mode_set(mode=previous_mode)
|
bpy.ops.object.mode_set(mode=previous_mode)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def hex_to_color(hex):
|
def hex_to_color(hex):
|
||||||
|
|
||||||
hex = hex.strip('#')
|
hex = hex.strip('#')
|
||||||
lv = len(hex)
|
lv = len(hex)
|
||||||
fin = list(int(hex[i:i + lv // 3], 16) for i in range(0, lv, lv // 3))
|
fin = list(int(hex[i:i + lv // 3], 16) for i in range(0, lv, lv // 3))
|
||||||
@ -205,23 +194,21 @@ def hex_to_color(hex):
|
|||||||
return tuple(fin)
|
return tuple(fin)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def color_to_hex(color):
|
def color_to_hex(color):
|
||||||
rgb = []
|
rgb = []
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
rgb.append( pow(color[i] , 1.0/gamma) )
|
rgb.append(pow(color[i], 1.0/gamma))
|
||||||
|
|
||||||
r = int(rgb[0]*255)
|
r = int(rgb[0]*255)
|
||||||
g = int(rgb[1]*255)
|
g = int(rgb[1]*255)
|
||||||
b = int(rgb[2]*255)
|
b = int(rgb[2]*255)
|
||||||
|
|
||||||
return "#{:02X}{:02X}{:02X}".format(r,g,b)
|
return "#{:02X}{:02X}{:02X}".format(r, g, b)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_color_id(index, count):
|
def get_color_id(index, count):
|
||||||
# Get unique color
|
# Get unique color
|
||||||
color = Color()
|
color = Color()
|
||||||
color.hsv = ( index / (count) ), 0.9, 1.0
|
color.hsv = (index / (count)), 0.9, 1.0
|
||||||
|
|
||||||
return color
|
return color
|
||||||
|
@ -11,20 +11,21 @@ from . import op_bake
|
|||||||
preview_collections = {}
|
preview_collections = {}
|
||||||
|
|
||||||
size_textures = [
|
size_textures = [
|
||||||
('32', '32', ''),
|
('32', '32', ''),
|
||||||
('64', '64', ''),
|
('64', '64', ''),
|
||||||
('128', '128', ''),
|
('128', '128', ''),
|
||||||
('256', '256', ''),
|
('256', '256', ''),
|
||||||
('512', '512', ''),
|
('512', '512', ''),
|
||||||
('1024', '1024', ''),
|
('1024', '1024', ''),
|
||||||
('2048', '2048', ''),
|
('2048', '2048', ''),
|
||||||
('4096', '4096', ''),
|
('4096', '4096', ''),
|
||||||
('8192', '8192', '')
|
('8192', '8192', '')
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
preview_icons = bpy.utils.previews.new()
|
preview_icons = bpy.utils.previews.new()
|
||||||
|
|
||||||
|
|
||||||
def icon_get(name):
|
def icon_get(name):
|
||||||
return preview_icons[name].icon_id
|
return preview_icons[name].icon_id
|
||||||
|
|
||||||
@ -33,11 +34,12 @@ def GetContextView3D():
|
|||||||
for window in bpy.context.window_manager.windows:
|
for window in bpy.context.window_manager.windows:
|
||||||
screen = window.screen
|
screen = window.screen
|
||||||
for area in screen.areas:
|
for area in screen.areas:
|
||||||
if area.type == 'VIEW_3D':
|
if area.type == 'VIEW_3D':
|
||||||
for region in area.regions:
|
for region in area.regions:
|
||||||
if region.type == 'WINDOW':
|
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,
|
||||||
return override
|
'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
|
return None
|
||||||
|
|
||||||
|
|
||||||
@ -45,86 +47,80 @@ def GetContextViewUV():
|
|||||||
for window in bpy.context.window_manager.windows:
|
for window in bpy.context.window_manager.windows:
|
||||||
screen = window.screen
|
screen = window.screen
|
||||||
for area in screen.areas:
|
for area in screen.areas:
|
||||||
if area.type == 'IMAGE_EDITOR':
|
if area.type == 'IMAGE_EDITOR':
|
||||||
for region in area.regions:
|
for region in area.regions:
|
||||||
if region.type == 'WINDOW':
|
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,
|
||||||
return override
|
'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
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def icon_register(fileName):
|
def icon_register(fileName):
|
||||||
name = fileName.split('.')[0] # Don't include file extension
|
name = fileName.split('.')[0] # Don't include file extension
|
||||||
icons_dir = os.path.join(os.path.dirname(__file__), "icons")
|
icons_dir = os.path.join(os.path.dirname(__file__), "icons")
|
||||||
preview_icons.load(name, os.path.join(icons_dir, fileName), 'IMAGE')
|
preview_icons.load(name, os.path.join(icons_dir, fileName), 'IMAGE')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_padding():
|
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
|
return bpy.context.scene.texToolsSettings.padding / size_min
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def generate_bake_mode_previews():
|
def generate_bake_mode_previews():
|
||||||
# We are accessing all of the information that we generated in the register function below
|
# We are accessing all of the information that we generated in the register function below
|
||||||
preview_collection = preview_collections["thumbnail_previews"]
|
preview_collection = preview_collections["thumbnail_previews"]
|
||||||
image_location = preview_collection.images_location
|
image_location = preview_collection.images_location
|
||||||
VALID_EXTENSIONS = ('.png', '.jpg', '.jpeg')
|
VALID_EXTENSIONS = ('.png', '.jpg', '.jpeg')
|
||||||
|
|
||||||
enum_items = []
|
enum_items = []
|
||||||
|
|
||||||
# Generate the thumbnails
|
# Generate the thumbnails
|
||||||
for i, image in enumerate(os.listdir(image_location)):
|
for i, image in enumerate(os.listdir(image_location)):
|
||||||
mode = image[0:-4]
|
mode = image[0:-4]
|
||||||
print(".. .{}".format(mode))
|
print(".. .{}".format(mode))
|
||||||
|
|
||||||
|
|
||||||
if image.endswith(VALID_EXTENSIONS) and mode in op_bake.modes:
|
if image.endswith(VALID_EXTENSIONS) and mode in op_bake.modes:
|
||||||
filepath = os.path.join(image_location, image)
|
filepath = os.path.join(image_location, image)
|
||||||
thumb = preview_collection.load(filepath, filepath, 'IMAGE')
|
thumb = preview_collection.load(filepath, filepath, 'IMAGE')
|
||||||
enum_items.append((image, mode, "", thumb.icon_id, i))
|
enum_items.append((image, mode, "", thumb.icon_id, i))
|
||||||
|
|
||||||
return enum_items
|
return enum_items
|
||||||
|
|
||||||
|
|
||||||
def get_bake_mode():
|
def get_bake_mode():
|
||||||
return str(bpy.context.scene.TT_bake_mode).replace(".png","").lower()
|
return str(bpy.context.scene.TT_bake_mode).replace(".png", "").lower()
|
||||||
|
|
||||||
|
|
||||||
class op_popup(bpy.types.Operator):
|
class op_popup(bpy.types.Operator):
|
||||||
bl_idname = "ui.textools_popup"
|
bl_idname = "ui.textools_popup"
|
||||||
bl_label = "Message"
|
bl_label = "Message"
|
||||||
|
|
||||||
message : StringProperty()
|
message: StringProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
self.report({'INFO'}, self.message)
|
self.report({'INFO'}, self.message)
|
||||||
print(self.message)
|
print(self.message)
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
wm = context.window_manager
|
wm = context.window_manager
|
||||||
return wm.invoke_popup(self, width=200, height=200)
|
return wm.invoke_popup(self, width=200, height=200)
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
self.layout.label(text=self.message)
|
self.layout.label(text=self.message)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def on_bakemode_set(self, context):
|
def on_bakemode_set(self, context):
|
||||||
print("Set '{}'".format(bpy.context.scene.TT_bake_mode))
|
print("Set '{}'".format(bpy.context.scene.TT_bake_mode))
|
||||||
utilities_bake.on_select_bake_mode(get_bake_mode())
|
utilities_bake.on_select_bake_mode(get_bake_mode())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
from bpy.types import Scene
|
from bpy.types import Scene
|
||||||
from bpy.props import StringProperty, EnumProperty
|
from bpy.props import StringProperty, EnumProperty
|
||||||
|
|
||||||
print("_______REgister previews")
|
print("_______REgister previews")
|
||||||
|
|
||||||
# Operators
|
# Operators
|
||||||
@ -135,19 +131,19 @@ def register():
|
|||||||
|
|
||||||
# Create a new preview collection (only upon register)
|
# Create a new preview collection (only upon register)
|
||||||
preview_collection = bpy.utils.previews.new()
|
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
|
preview_collections["thumbnail_previews"] = preview_collection
|
||||||
|
|
||||||
|
|
||||||
# This is an EnumProperty to hold all of the images
|
# 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
|
# You really can save it anywhere in bpy.types.* Just make sure the location makes sense
|
||||||
bpy.types.Scene.TT_bake_mode = EnumProperty(
|
bpy.types.Scene.TT_bake_mode = EnumProperty(
|
||||||
items=generate_bake_mode_previews(),
|
items=generate_bake_mode_previews(),
|
||||||
update = on_bakemode_set,
|
update=on_bakemode_set,
|
||||||
default = 'normal_tangent.png'
|
default='normal_tangent.png'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
|
|
||||||
print("_______UNregister previews")
|
print("_______UNregister previews")
|
||||||
@ -156,15 +152,14 @@ def unregister():
|
|||||||
for preview_collection in preview_collections.values():
|
for preview_collection in preview_collections.values():
|
||||||
bpy.utils.previews.remove(preview_collection)
|
bpy.utils.previews.remove(preview_collection)
|
||||||
preview_collections.clear()
|
preview_collections.clear()
|
||||||
|
|
||||||
|
|
||||||
# Unregister icons
|
# Unregister icons
|
||||||
# global preview_icons
|
# global preview_icons
|
||||||
bpy.utils.previews.remove(preview_icons)
|
bpy.utils.previews.remove(preview_icons)
|
||||||
|
|
||||||
|
|
||||||
del bpy.types.Scene.TT_bake_mode
|
del bpy.types.Scene.TT_bake_mode
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
register()
|
register()
|
||||||
bpy.utils.register_class(op_popup)
|
bpy.utils.register_class(op_popup)
|
||||||
|
Loading…
Reference in New Issue
Block a user