From 8ade49a37bce8421559a9d2fa336eb28d29abf00 Mon Sep 17 00:00:00 2001 From: passivestar <60579014+passivestar@users.noreply.github.com> Date: Fri, 11 Oct 2024 21:48:06 +0400 Subject: [PATCH] Implement uniform scale for layer and mask UVs (#196) --- Layer.py | 24 +++++++++++++++++++++- Mask.py | 25 ++++++++++++++++++++++- common.py | 11 ++++++++++ input_outputs.py | 8 ++++++++ node_connections.py | 18 ++++++++++++++++ preferences.py | 8 ++++++++ ui.py | 50 +++++++++++++++++++++++++++++++++++---------- 7 files changed, 131 insertions(+), 13 deletions(-) diff --git a/Layer.py b/Layer.py index 06c3c6d4..988301d7 100644 --- a/Layer.py +++ b/Layer.py @@ -70,7 +70,7 @@ def add_new_layer(group_tree, layer_name, layer_type, channel_idx, ): yp = group_tree.yp - #ypup = get_user_preferences() + ypup = get_user_preferences() obj = bpy.context.object mat = obj.active_material @@ -149,6 +149,10 @@ def add_new_layer(group_tree, layer_name, layer_type, channel_idx, # Tree start and end create_essential_nodes(tree, True, False, True) + # Uniform Scale + if is_bl_newer_than(2, 81) and is_layer_using_vector(layer): + layer.enable_uniform_scale = ypup.enable_uniform_uv_scale_by_default + # Add source if layer_type == 'VCOL': source = new_node(tree, layer, 'source', get_vcol_bl_idname(), 'Source') @@ -5971,6 +5975,15 @@ def update_layer_blur_vector_factor(self, context): if blur_vector: blur_vector.inputs[0].default_value = layer.blur_vector_factor +def update_layer_uniform_scale_enabled(self, context): + if not hasattr(context, 'layer'): return + + update_entity_uniform_scale_enabled(self) + + check_layer_tree_ios(context.layer) + reconnect_layer_nodes(context.layer) + rearrange_layer_nodes(context.layer) + class YLayer(bpy.types.PropertyGroup): name : StringProperty( name = 'Layer Name', @@ -6185,6 +6198,15 @@ class YLayer(bpy.types.PropertyGroup): texcoord : StringProperty(default='') blur_vector : StringProperty(default='') + enable_uniform_scale : BoolProperty( + name = 'Enable Uniform Scale', + description = 'Use the same value for all scale components', + default = False, + update = update_layer_uniform_scale_enabled + ) + + uniform_scale_value : FloatProperty(default=1) + decal_process : StringProperty(default='') #need_temp_uv_refresh : BoolProperty(default=False) diff --git a/Mask.py b/Mask.py index 05296f19..7ba2d6d8 100644 --- a/Mask.py +++ b/Mask.py @@ -14,6 +14,7 @@ def add_new_mask(layer, name, mask_type, texcoord_type, uv_name, image = None, vcol = None, segment=None, object_index=0, blend_type='MULTIPLY', hemi_space='WORLD', hemi_use_prev_normal=False, color_id=(1,0,1), source_input='RGB', edge_detect_radius=0.05, modifier_type='INVERT', interpolation='Linear'): yp = layer.id_data.yp yp.halt_update = True + ypup = get_user_preferences() tree = get_tree(layer) nodes = tree.nodes @@ -24,6 +25,10 @@ def add_new_mask(layer, name, mask_type, texcoord_type, uv_name, image = None, v mask.texcoord_type = texcoord_type mask.source_input = source_input + # Uniform Scale + if is_bl_newer_than(2, 81) and is_mask_using_vector(mask): + mask.enable_uniform_scale = ypup.enable_uniform_uv_scale_by_default + if segment: mask.segment_name = segment.name @@ -403,7 +408,7 @@ def invoke(self, context, event): elif layer.type == 'IMAGE': source = get_layer_source(layer) if source and source.image: self.interpolation = source.interpolation - + if get_user_preferences().skip_property_popups and not event.shift: return self.execute(context) @@ -1735,6 +1740,15 @@ class YLayerMaskChannel(bpy.types.PropertyGroup): # UI related expand_content : BoolProperty(default=False) +def update_mask_uniform_scale_enabled(self, context): + if not hasattr(context, 'layer'): return + + update_entity_uniform_scale_enabled(self) + + check_layer_tree_ios(context.layer) + reconnect_layer_nodes(context.layer) + rearrange_layer_nodes(context.layer) + class YLayerMask(bpy.types.PropertyGroup): name : StringProperty(default='', update=update_mask_name) @@ -1944,6 +1958,15 @@ class YLayerMask(bpy.types.PropertyGroup): baked_mapping : StringProperty(default='') blur_vector : StringProperty(default='') + enable_uniform_scale : BoolProperty( + name = 'Enable Uniform Scale', + description = 'Use the same value for all scale components', + default = False, + update = update_mask_uniform_scale_enabled + ) + + uniform_scale_value : FloatProperty(default=1) + decal_process : StringProperty(default='') texcoord : StringProperty(default='') decal_alpha : StringProperty(default='') diff --git a/common.py b/common.py index 79fe68df..6944fd3a 100644 --- a/common.py +++ b/common.py @@ -2167,6 +2167,17 @@ def get_entity_mapping(entity, get_baked=False): return None +def update_entity_uniform_scale_enabled(entity): + scale_input = get_entity_mapping(entity).inputs[3] + + if entity.enable_uniform_scale: + # Set the uniform scale to min axis of regular scale when uniform scale is enabled + set_entity_prop_value(entity, 'uniform_scale_value', min(map(abs, scale_input.default_value))) + else: + # Set the regular scale axes to the uniform scale when uniform scale is disabled + scale = get_entity_prop_value(entity, 'uniform_scale_value') + scale_input.default_value = (scale, scale, scale) + def get_neighbor_uv_space_input(texcoord_type): if texcoord_type == 'UV': return 0.0 # Tangent Space diff --git a/input_outputs.py b/input_outputs.py index b7d0e1b9..1724dc9b 100644 --- a/input_outputs.py +++ b/input_outputs.py @@ -844,6 +844,10 @@ def check_layer_tree_ios(layer, tree=None, remove_props=False, hard_reset=False) dirty = create_prop_input(layer, 'decal_distance_value', valid_inputs, input_index, dirty) input_index += 1 + if is_bl_newer_than(2, 81) and layer.enable_uniform_scale and is_layer_using_vector(layer): + dirty = create_prop_input(layer, 'uniform_scale_value', valid_inputs, input_index, dirty) + input_index += 1 + # Channel prop inputs for i, ch in enumerate(layer.channels): if not get_channel_enabled(ch): continue @@ -960,6 +964,10 @@ def check_layer_tree_ios(layer, tree=None, remove_props=False, hard_reset=False) dirty = create_prop_input(mask, 'intensity_value', valid_inputs, input_index, dirty) input_index += 1 + if is_bl_newer_than(2, 81) and mask.enable_uniform_scale and is_mask_using_vector(mask): + dirty = create_prop_input(mask, 'uniform_scale_value', valid_inputs, input_index, dirty) + input_index += 1 + # Mask blur vector if mask.enable_blur_vector: dirty = create_prop_input(mask, 'blur_vector_factor', valid_inputs, input_index, dirty) diff --git a/node_connections.py b/node_connections.py index d0b617fc..7ae48aeb 100644 --- a/node_connections.py +++ b/node_connections.py @@ -1818,6 +1818,15 @@ def reconnect_layer_nodes(layer, ch_idx=-1, merge_mask=False): if vector and mapping and layer.texcoord_type != 'Decal': vector = create_link(tree, vector, mapping.inputs[0])[0] + + # Layer UV uniform scale value + if is_bl_newer_than(2, 81): + uniform_scale_value = get_essential_node(tree, TREE_START).get(get_entity_input_name(layer, 'uniform_scale_value')) + if uniform_scale_value: + if layer.enable_uniform_scale: + create_link(tree, uniform_scale_value, mapping.inputs[3]) + else: + break_link(tree, uniform_scale_value, mapping.inputs[3]) if vector and layer.type not in {'VCOL', 'BACKGROUND', 'COLOR', 'GROUP', 'HEMI', 'OBJECT_INDEX'}: create_link(tree, vector, source.inputs[0]) @@ -2082,6 +2091,15 @@ def reconnect_layer_nodes(layer, ch_idx=-1, merge_mask=False): create_link(tree, mask_vector, mask_source.inputs[0]) + # Mask UV uniform scale value + if is_bl_newer_than(2, 81): + uniform_scale_value = get_essential_node(tree, TREE_START).get(get_entity_input_name(mask, 'uniform_scale_value')) + if uniform_scale_value: + if mask.enable_uniform_scale: + create_link(tree, uniform_scale_value, mask_mapping.inputs[3]) + else: + break_link(tree, uniform_scale_value, mask_mapping.inputs[3]) + # Mask uv neighbor mask_uv_neighbor = nodes.get(mask.uv_neighbor) if mask.texcoord_type != 'Layer' else uv_neighbor if mask_uv_neighbor: diff --git a/preferences.py b/preferences.py index 1186dc97..3c70919a 100644 --- a/preferences.py +++ b/preferences.py @@ -92,6 +92,12 @@ class YPaintPreferences(AddonPreferences): description = "Enable baked outside by default when creating new "+get_addon_title()+" node.\n(Useful for creating game assets)", default = False ) + + enable_uniform_uv_scale_by_default : BoolProperty( + name = 'Enable Uniform UV Scale by default', + description = "Enable uniform UV scale by default in Layer and Mask UVs. This will make all scale axes have the same value", + default = False + ) # Addon updater preferences. auto_check_update : BoolProperty( @@ -139,6 +145,8 @@ def draw(self, context): self.layout.prop(self, 'use_image_preview') self.layout.prop(self, 'skip_property_popups') self.layout.prop(self, 'enable_baked_outside_by_default') + if is_bl_newer_than(2, 81): + self.layout.prop(self, 'enable_uniform_uv_scale_by_default') self.layout.prop(self, 'show_experimental') self.layout.prop(self, 'developer_mode') #self.layout.prop(self, 'parallax_without_baked') diff --git a/ui.py b/ui.py index 620eead1..f2550895 100644 --- a/ui.py +++ b/ui.py @@ -571,14 +571,14 @@ def draw_inbetween_modifier_mask_props(layer, source, layout): elif layer.modifier_type == 'RAMP': col.template_color_ramp(source, "color_ramp", expand=True) -def draw_input_prop(layout, entity, prop_name, emboss=None): +def draw_input_prop(layout, entity, prop_name, emboss=None, text=''): inp = get_entity_prop_input(entity, prop_name) if emboss != None: - if inp: layout.prop(inp, 'default_value', text='', emboss=emboss) - else: layout.prop(entity, prop_name, text='', emboss=emboss) + if inp: layout.prop(inp, 'default_value', text=text, emboss=emboss) + else: layout.prop(entity, prop_name, text=text, emboss=emboss) else: - if inp: layout.prop(inp, 'default_value', text='') - else: layout.prop(entity, prop_name, text='') + if inp: layout.prop(inp, 'default_value', text=text) + else: layout.prop(entity, prop_name, text=text) def draw_mask_modifier_stack(layer, mask, layout, ui): ypui = bpy.context.window_manager.ypui @@ -1476,8 +1476,15 @@ def draw_layer_source(context, layout, layer, layer_tree, source, image, vcol, i mcol.prop(mapping.inputs[1], 'default_value', text='Offset') mcol = rrow.column() mcol.prop(mapping.inputs[2], 'default_value', text='Rotation') - mcol = rrow.column() - mcol.prop(mapping.inputs[3], 'default_value', text='Scale') + if layer.enable_uniform_scale: + mcol = rrow.column(align=True) + mcol.label(text='Scale:') + draw_input_prop(mcol, layer, 'uniform_scale_value', None, 'X') + draw_input_prop(mcol, layer, 'uniform_scale_value', None, 'Y') + draw_input_prop(mcol, layer, 'uniform_scale_value', None, 'Z') + else: + mcol = rrow.column() + mcol.prop(mapping.inputs[3], 'default_value', text='Scale') else: mcol = rrow.column() mcol.prop(mapping, 'translation') @@ -1485,6 +1492,13 @@ def draw_layer_source(context, layout, layer, layer_tree, source, image, vcol, i mcol.prop(mapping, 'rotation') mcol = rrow.column() mcol.prop(mapping, 'scale') + + # Uniform scale + if is_bl_newer_than(2, 81): + rrow = boxcol.row(align=True) + splits = split_layout(rrow, 0.5) + splits.label(text='Uniform Scale:') + rrow.prop(layer, 'enable_uniform_scale', text='') if yp.need_temp_uv_refresh: rrow = boxcol.row(align=True) @@ -1493,7 +1507,7 @@ def draw_layer_source(context, layout, layer, layer_tree, source, image, vcol, i # Blur row rrow = boxcol.row(align=True) - splits = split_layout(rrow, 0.3) + splits = split_layout(rrow, 0.5) splits.label(text='Blur:') if layer.enable_blur_vector: draw_input_prop(splits, layer, 'blur_vector_factor') @@ -2446,8 +2460,15 @@ def draw_layer_masks(context, layout, layer): mcol.prop(mapping.inputs[1], 'default_value', text='Offset') mcol = rrow.column() mcol.prop(mapping.inputs[2], 'default_value', text='Rotation') - mcol = rrow.column() - mcol.prop(mapping.inputs[3], 'default_value', text='Scale') + if mask.enable_uniform_scale: + mcol = rrow.column(align=True) + mcol.label(text='Scale:') + draw_input_prop(mcol, mask, 'uniform_scale_value', None, 'X') + draw_input_prop(mcol, mask, 'uniform_scale_value', None, 'Y') + draw_input_prop(mcol, mask, 'uniform_scale_value', None, 'Z') + else: + mcol = rrow.column() + mcol.prop(mapping.inputs[3], 'default_value', text='Scale') else: mcol = rrow.column() mcol.prop(mapping, 'translation') @@ -2455,6 +2476,13 @@ def draw_layer_masks(context, layout, layer): mcol.prop(mapping, 'rotation') mcol = rrow.column() mcol.prop(mapping, 'scale') + + # Uniform scale + if is_bl_newer_than(2, 81): + rrow = boxcol.row(align=True) + splits = split_layout(rrow, 0.5) + splits.label(text='Uniform Scale:') + rrow.prop(mask, 'enable_uniform_scale', text='') if mask.type == 'IMAGE' and mask.active_edit and ( yp.need_temp_uv_refresh @@ -2466,7 +2494,7 @@ def draw_layer_masks(context, layout, layer): # Blur row if mask.texcoord_type != 'Layer': rrow = boxcol.row(align=True) - splits = split_layout(rrow, 0.3) + splits = split_layout(rrow, 0.5) splits.label(text='Blur:') if mask.enable_blur_vector: draw_input_prop(splits, mask, 'blur_vector_factor')