mirror of
https://github.com/Orama-Interactive/Pixelorama.git
synced 2025-01-18 17:19:50 +00:00
Implement basic clipping masks
A very simple implementation, not as complex as something like #768 yet, but it can be done in the future. The main current limitation is that it doesn't work with group layers as of right now.
This commit is contained in:
parent
fdc92ccfc3
commit
c1b78e4c01
BIN
assets/graphics/layers/clipping_mask.png
Normal file
BIN
assets/graphics/layers/clipping_mask.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 148 B |
34
assets/graphics/layers/clipping_mask.png.import
Normal file
34
assets/graphics/layers/clipping_mask.png.import
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://ieo8fsapcgsy"
|
||||||
|
path="res://.godot/imported/clipping_mask.png-735677b4fff2e062e79993566d07bdd3.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/graphics/layers/clipping_mask.png"
|
||||||
|
dest_files=["res://.godot/imported/clipping_mask.png-735677b4fff2e062e79993566d07bdd3.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
|
@ -26,9 +26,10 @@ func blend_layers(
|
||||||
only_selected_layers := false,
|
only_selected_layers := false,
|
||||||
) -> void:
|
) -> void:
|
||||||
var textures: Array[Image] = []
|
var textures: Array[Image] = []
|
||||||
# Nx3 texture, where N is the number of layers and the first row are the blend modes,
|
# Nx4 texture, where N is the number of layers and the first row are the blend modes,
|
||||||
# the second are the opacities and the third are the origins
|
# the second are the opacities, the third are the origins and the fourth are the
|
||||||
var metadata_image := Image.create(project.layers.size(), 3, false, Image.FORMAT_R8)
|
# clipping mask booleans.
|
||||||
|
var metadata_image := Image.create(project.layers.size(), 4, false, Image.FORMAT_R8)
|
||||||
var frame_index := project.frames.find(frame)
|
var frame_index := project.frames.find(frame)
|
||||||
var previous_ordered_layers: Array[int] = project.ordered_layers
|
var previous_ordered_layers: Array[int] = project.ordered_layers
|
||||||
project.order_layers(frame_index)
|
project.order_layers(frame_index)
|
||||||
|
@ -51,14 +52,7 @@ func blend_layers(
|
||||||
var cel := frame.cels[ordered_index]
|
var cel := frame.cels[ordered_index]
|
||||||
var cel_image := layer.display_effects(cel)
|
var cel_image := layer.display_effects(cel)
|
||||||
textures.append(cel_image)
|
textures.append(cel_image)
|
||||||
# Store the blend mode
|
set_layer_metadata_image(layer, cel, metadata_image, ordered_index, include)
|
||||||
metadata_image.set_pixel(ordered_index, 0, Color(layer.blend_mode / 255.0, 0.0, 0.0, 0.0))
|
|
||||||
# Store the opacity
|
|
||||||
if include:
|
|
||||||
var opacity := cel.get_final_opacity(layer)
|
|
||||||
metadata_image.set_pixel(ordered_index, 1, Color(opacity, 0.0, 0.0, 0.0))
|
|
||||||
else:
|
|
||||||
metadata_image.set_pixel(ordered_index, 1, Color())
|
|
||||||
var texture_array := Texture2DArray.new()
|
var texture_array := Texture2DArray.new()
|
||||||
texture_array.create_from_images(textures)
|
texture_array.create_from_images(textures)
|
||||||
var params := {
|
var params := {
|
||||||
|
@ -73,6 +67,24 @@ func blend_layers(
|
||||||
project.ordered_layers = previous_ordered_layers
|
project.ordered_layers = previous_ordered_layers
|
||||||
|
|
||||||
|
|
||||||
|
func set_layer_metadata_image(
|
||||||
|
layer: BaseLayer, cel: BaseCel, image: Image, index: int, include := true
|
||||||
|
) -> void:
|
||||||
|
# Store the blend mode
|
||||||
|
image.set_pixel(index, 0, Color(layer.blend_mode / 255.0, 0.0, 0.0, 0.0))
|
||||||
|
# Store the opacity
|
||||||
|
if layer.is_visible_in_hierarchy() and include:
|
||||||
|
var opacity := cel.get_final_opacity(layer)
|
||||||
|
image.set_pixel(index, 1, Color(opacity, 0.0, 0.0, 0.0))
|
||||||
|
else:
|
||||||
|
image.set_pixel(index, 1, Color())
|
||||||
|
# Store the clipping mask boolean
|
||||||
|
if layer.clipping_mask:
|
||||||
|
image.set_pixel(index, 3, Color.WHITE)
|
||||||
|
else:
|
||||||
|
image.set_pixel(index, 3, Color.BLACK)
|
||||||
|
|
||||||
|
|
||||||
## Algorithm based on http://members.chello.at/easyfilter/bresenham.html
|
## Algorithm based on http://members.chello.at/easyfilter/bresenham.html
|
||||||
func get_ellipse_points(pos: Vector2i, size: Vector2i) -> Array[Vector2i]:
|
func get_ellipse_points(pos: Vector2i, size: Vector2i) -> Array[Vector2i]:
|
||||||
var array: Array[Vector2i] = []
|
var array: Array[Vector2i] = []
|
||||||
|
|
|
@ -36,6 +36,7 @@ var visible := true ## Sets visibility of the layer.
|
||||||
var locked := false ## Images of a locked layer won't be overritten.
|
var locked := false ## Images of a locked layer won't be overritten.
|
||||||
var new_cels_linked := false ## Determines if new cel of the layer should be linked or not.
|
var new_cels_linked := false ## Determines if new cel of the layer should be linked or not.
|
||||||
var blend_mode := BlendModes.NORMAL ## Blend mode of the current layer.
|
var blend_mode := BlendModes.NORMAL ## Blend mode of the current layer.
|
||||||
|
var clipping_mask := false ## If [code]true[/code], the layer acts as a clipping mask.
|
||||||
var opacity := 1.0 ## The opacity of the layer, affects all frames that belong to that layer.
|
var opacity := 1.0 ## The opacity of the layer, affects all frames that belong to that layer.
|
||||||
var cel_link_sets: Array[Dictionary] = [] ## Each Dictionary represents a cel's "link set"
|
var cel_link_sets: Array[Dictionary] = [] ## Each Dictionary represents a cel's "link set"
|
||||||
var effects: Array[LayerEffect] ## An array for non-destructive effects of the layer.
|
var effects: Array[LayerEffect] ## An array for non-destructive effects of the layer.
|
||||||
|
@ -215,6 +216,7 @@ func serialize() -> Dictionary:
|
||||||
"visible": visible,
|
"visible": visible,
|
||||||
"locked": locked,
|
"locked": locked,
|
||||||
"blend_mode": blend_mode,
|
"blend_mode": blend_mode,
|
||||||
|
"clipping_mask": clipping_mask,
|
||||||
"opacity": opacity,
|
"opacity": opacity,
|
||||||
"parent": parent.index if is_instance_valid(parent) else -1,
|
"parent": parent.index if is_instance_valid(parent) else -1,
|
||||||
"effects": effect_data
|
"effects": effect_data
|
||||||
|
@ -233,13 +235,12 @@ func serialize() -> Dictionary:
|
||||||
|
|
||||||
## Sets the layer data according to a curated [Dictionary] obtained from [method serialize].
|
## Sets the layer data according to a curated [Dictionary] obtained from [method serialize].
|
||||||
func deserialize(dict: Dictionary) -> void:
|
func deserialize(dict: Dictionary) -> void:
|
||||||
name = dict.name
|
name = dict.get("name", "")
|
||||||
visible = dict.visible
|
visible = dict.get("visible", true)
|
||||||
locked = dict.locked
|
locked = dict.get("locked", false)
|
||||||
if dict.has("blend_mode"):
|
blend_mode = dict.get("blend_mode", BlendModes.NORMAL)
|
||||||
blend_mode = dict.blend_mode
|
clipping_mask = dict.get("clipping_mask", false)
|
||||||
if dict.has("opacity"):
|
opacity = dict.get("opacity", 1.0)
|
||||||
opacity = dict.opacity
|
|
||||||
if dict.get("parent", -1) != -1:
|
if dict.get("parent", -1) != -1:
|
||||||
parent = project.layers[dict.parent]
|
parent = project.layers[dict.parent]
|
||||||
if dict.has("linked_cels") and not dict["linked_cels"].is_empty(): # Backwards compatibility
|
if dict.has("linked_cels") and not dict["linked_cels"].is_empty(): # Backwards compatibility
|
||||||
|
|
|
@ -5,8 +5,9 @@ const float HCV_EPSILON = 1e-10;
|
||||||
const float HSL_EPSILON = 1e-10;
|
const float HSL_EPSILON = 1e-10;
|
||||||
|
|
||||||
uniform sampler2DArray layers : filter_nearest;
|
uniform sampler2DArray layers : filter_nearest;
|
||||||
// Nx3 texture, where N is the number of layers and the first row are the blend modes,
|
// Nx4 texture, where N is the number of layers and the first row are the blend modes,
|
||||||
// the second are the opacities and the third are the origins
|
// the second are the opacities, the third are the origins and the fourth are the
|
||||||
|
// clipping mask booleans.
|
||||||
uniform sampler2D metadata : filter_nearest;
|
uniform sampler2D metadata : filter_nearest;
|
||||||
uniform bool origin_x_positive = true;
|
uniform bool origin_x_positive = true;
|
||||||
uniform bool origin_y_positive = true;
|
uniform bool origin_y_positive = true;
|
||||||
|
@ -153,9 +154,9 @@ void fragment() {
|
||||||
first_origin.y = -first_origin.y;
|
first_origin.y = -first_origin.y;
|
||||||
}
|
}
|
||||||
float first_opacity = texture(metadata, vec2(0.0, 1.0 / float(metadata_size.y))).r;
|
float first_opacity = texture(metadata, vec2(0.0, 1.0 / float(metadata_size.y))).r;
|
||||||
vec4 col = texture(layers, vec3(UV - first_origin, 0.0));
|
vec4 result_color = texture(layers, vec3(UV - first_origin, 0.0));
|
||||||
col.a = border_trim(col, UV - first_origin);
|
result_color.a = border_trim(result_color, UV - first_origin);
|
||||||
col.a *= first_opacity;
|
result_color.a *= first_opacity;
|
||||||
for(int i = 1; i < metadata_size.x + 1; i++) // Loops through every layer
|
for(int i = 1; i < metadata_size.x + 1; i++) // Loops through every layer
|
||||||
{
|
{
|
||||||
float blend_mode_float = texture(metadata, vec2(float(i) / float(metadata_size.x), 0.0)).r;
|
float blend_mode_float = texture(metadata, vec2(float(i) / float(metadata_size.x), 0.0)).r;
|
||||||
|
@ -171,9 +172,12 @@ void fragment() {
|
||||||
}
|
}
|
||||||
float current_opacity = texture(metadata, vec2(float(i) / float(metadata_size.x), 1.0 / float(metadata_size.y))).r;
|
float current_opacity = texture(metadata, vec2(float(i) / float(metadata_size.x), 1.0 / float(metadata_size.y))).r;
|
||||||
vec2 uv = UV - current_origin;
|
vec2 uv = UV - current_origin;
|
||||||
vec4 texture_color = texture(layers, vec3(uv, float(i)));
|
vec4 layer_color = texture(layers, vec3(uv, float(i)));
|
||||||
texture_color.a = border_trim(texture_color, uv);
|
vec4 prev_layer_color = texture(layers, vec3(uv, float(i - 1)));
|
||||||
col = blend(current_blend_mode, texture_color, col, current_opacity);
|
float clipping_mask = texture(metadata, vec2(float(i) / float(metadata_size.x), 3.0 / float(metadata_size.y))).r;
|
||||||
|
layer_color.a *= prev_layer_color.a * step(0.5, clipping_mask) + 1.0 * step(clipping_mask, 0.5);
|
||||||
|
layer_color.a = border_trim(layer_color, uv);
|
||||||
|
result_color = blend(current_blend_mode, layer_color, result_color, current_opacity);
|
||||||
}
|
}
|
||||||
COLOR = col;
|
COLOR = result_color;
|
||||||
}
|
}
|
||||||
|
|
|
@ -148,9 +148,10 @@ func draw_layers() -> void:
|
||||||
if recreate_texture_array:
|
if recreate_texture_array:
|
||||||
var textures: Array[Image] = []
|
var textures: Array[Image] = []
|
||||||
textures.resize(project.layers.size())
|
textures.resize(project.layers.size())
|
||||||
# Nx3 texture, where N is the number of layers and the first row are the blend modes,
|
# Nx4 texture, where N is the number of layers and the first row are the blend modes,
|
||||||
# the second are the opacities and the third are the origins
|
# the second are the opacities, the third are the origins and the fourth are the
|
||||||
layer_metadata_image = Image.create(project.layers.size(), 3, false, Image.FORMAT_RG8)
|
# clipping mask booleans.
|
||||||
|
layer_metadata_image = Image.create(project.layers.size(), 4, false, Image.FORMAT_RG8)
|
||||||
# Draw current frame layers
|
# Draw current frame layers
|
||||||
for i in project.layers.size():
|
for i in project.layers.size():
|
||||||
var ordered_index := project.ordered_layers[i]
|
var ordered_index := project.ordered_layers[i]
|
||||||
|
@ -162,16 +163,7 @@ func draw_layers() -> void:
|
||||||
else:
|
else:
|
||||||
cel_image = cel.get_image()
|
cel_image = cel.get_image()
|
||||||
textures[ordered_index] = cel_image
|
textures[ordered_index] = cel_image
|
||||||
# Store the blend mode
|
DrawingAlgos.set_layer_metadata_image(layer, cel, layer_metadata_image, ordered_index)
|
||||||
layer_metadata_image.set_pixel(
|
|
||||||
ordered_index, 0, Color(layer.blend_mode / 255.0, 0.0, 0.0, 0.0)
|
|
||||||
)
|
|
||||||
# Store the opacity
|
|
||||||
if layer.is_visible_in_hierarchy():
|
|
||||||
var opacity := cel.get_final_opacity(layer)
|
|
||||||
layer_metadata_image.set_pixel(ordered_index, 1, Color(opacity, 0.0, 0.0, 0.0))
|
|
||||||
else:
|
|
||||||
layer_metadata_image.set_pixel(ordered_index, 1, Color())
|
|
||||||
# Store the origin
|
# Store the origin
|
||||||
if [project.current_frame, i] in project.selected_cels:
|
if [project.current_frame, i] in project.selected_cels:
|
||||||
var origin := Vector2(move_preview_location).abs() / Vector2(cel_image.get_size())
|
var origin := Vector2(move_preview_location).abs() / Vector2(cel_image.get_size())
|
||||||
|
@ -199,14 +191,10 @@ func draw_layers() -> void:
|
||||||
else:
|
else:
|
||||||
cel_image = cel.get_image()
|
cel_image = cel.get_image()
|
||||||
layer_texture_array.update_layer(cel_image, ordered_index)
|
layer_texture_array.update_layer(cel_image, ordered_index)
|
||||||
layer_metadata_image.set_pixel(
|
DrawingAlgos.set_layer_metadata_image(
|
||||||
ordered_index, 0, Color(layer.blend_mode / 255.0, 0.0, 0.0, 0.0)
|
layer, cel, layer_metadata_image, ordered_index
|
||||||
)
|
)
|
||||||
if layer.is_visible_in_hierarchy():
|
# Update the origin
|
||||||
var opacity := cel.get_final_opacity(layer)
|
|
||||||
layer_metadata_image.set_pixel(ordered_index, 1, Color(opacity, 0.0, 0.0, 0.0))
|
|
||||||
else:
|
|
||||||
layer_metadata_image.set_pixel(ordered_index, 1, Color())
|
|
||||||
var origin := Vector2(move_preview_location).abs() / Vector2(cel_image.get_size())
|
var origin := Vector2(move_preview_location).abs() / Vector2(cel_image.get_size())
|
||||||
layer_metadata_image.set_pixel(
|
layer_metadata_image.set_pixel(
|
||||||
ordered_index, 2, Color(origin.x, origin.y, 0.0, 0.0)
|
ordered_index, 2, Color(origin.x, origin.y, 0.0, 0.0)
|
||||||
|
|
|
@ -76,9 +76,10 @@ func _draw_layers() -> void:
|
||||||
var current_frame := project.frames[frame_index]
|
var current_frame := project.frames[frame_index]
|
||||||
var current_cels := current_frame.cels
|
var current_cels := current_frame.cels
|
||||||
var textures: Array[Image] = []
|
var textures: Array[Image] = []
|
||||||
# Nx3 texture, where N is the number of layers and the first row are the blend modes,
|
# Nx4 texture, where N is the number of layers and the first row are the blend modes,
|
||||||
# the second are the opacities and the third are the origins
|
# the second are the opacities, the third are the origins and the fourth are the
|
||||||
var metadata_image := Image.create(project.layers.size(), 3, false, Image.FORMAT_R8)
|
# clipping mask booleans.
|
||||||
|
var metadata_image := Image.create(project.layers.size(), 4, false, Image.FORMAT_R8)
|
||||||
# Draw current frame layers
|
# Draw current frame layers
|
||||||
for i in project.ordered_layers:
|
for i in project.ordered_layers:
|
||||||
var cel := current_cels[i]
|
var cel := current_cels[i]
|
||||||
|
@ -91,12 +92,7 @@ func _draw_layers() -> void:
|
||||||
else:
|
else:
|
||||||
cel_image = cel.get_image()
|
cel_image = cel.get_image()
|
||||||
textures.append(cel_image)
|
textures.append(cel_image)
|
||||||
metadata_image.set_pixel(i, 0, Color(layer.blend_mode / 255.0, 0.0, 0.0, 0.0))
|
DrawingAlgos.set_layer_metadata_image(layer, cel, metadata_image, i)
|
||||||
if layer.is_visible_in_hierarchy():
|
|
||||||
var opacity := cel.get_final_opacity(layer)
|
|
||||||
metadata_image.set_pixel(i, 1, Color(opacity, 0.0, 0.0, 0.0))
|
|
||||||
else:
|
|
||||||
metadata_image.set_pixel(i, 1, Color(0.0, 0.0, 0.0, 0.0))
|
|
||||||
var texture_array := Texture2DArray.new()
|
var texture_array := Texture2DArray.new()
|
||||||
texture_array.create_from_images(textures)
|
texture_array.create_from_images(textures)
|
||||||
material.set_shader_parameter("layers", texture_array)
|
material.set_shader_parameter("layers", texture_array)
|
||||||
|
|
|
@ -940,13 +940,11 @@ func _on_MergeDownLayer_pressed() -> void:
|
||||||
var top_image := top_layer.display_effects(top_cel)
|
var top_image := top_layer.display_effects(top_cel)
|
||||||
var bottom_cel := frame.cels[bottom_layer.index]
|
var bottom_cel := frame.cels[bottom_layer.index]
|
||||||
var textures: Array[Image] = []
|
var textures: Array[Image] = []
|
||||||
var metadata_image := Image.create(2, 3, false, Image.FORMAT_R8)
|
|
||||||
textures.append(bottom_cel.get_image())
|
textures.append(bottom_cel.get_image())
|
||||||
metadata_image.set_pixel(0, 1, Color(1.0, 0.0, 0.0, 0.0))
|
|
||||||
textures.append(top_image)
|
textures.append(top_image)
|
||||||
metadata_image.set_pixel(1, 0, Color(top_layer.blend_mode / 255.0, 0.0, 0.0, 0.0))
|
var metadata_image := Image.create(2, 4, false, Image.FORMAT_R8)
|
||||||
var opacity := frame.cels[top_layer.index].get_final_opacity(top_layer)
|
DrawingAlgos.set_layer_metadata_image(bottom_layer, bottom_cel, metadata_image, 0)
|
||||||
metadata_image.set_pixel(1, 1, Color(opacity, 0.0, 0.0, 0.0))
|
DrawingAlgos.set_layer_metadata_image(top_layer, top_cel, metadata_image, 1)
|
||||||
var texture_array := Texture2DArray.new()
|
var texture_array := Texture2DArray.new()
|
||||||
texture_array.create_from_images(textures)
|
texture_array.create_from_images(textures)
|
||||||
var params := {
|
var params := {
|
||||||
|
|
|
@ -12,6 +12,8 @@ var layer_index := 0
|
||||||
@onready var line_edit := %LayerNameLineEdit as LineEdit
|
@onready var line_edit := %LayerNameLineEdit as LineEdit
|
||||||
@onready var hierarchy_spacer := %HierarchySpacer as Control
|
@onready var hierarchy_spacer := %HierarchySpacer as Control
|
||||||
@onready var linked_button := %LinkButton as BaseButton
|
@onready var linked_button := %LinkButton as BaseButton
|
||||||
|
@onready var clipping_mask_icon := %ClippingMask as TextureRect
|
||||||
|
@onready var popup_menu := $PopupMenu as PopupMenu
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
|
@ -71,6 +73,8 @@ func update_buttons() -> void:
|
||||||
|
|
||||||
visibility_button.modulate.a = 1
|
visibility_button.modulate.a = 1
|
||||||
lock_button.modulate.a = 1
|
lock_button.modulate.a = 1
|
||||||
|
popup_menu.set_item_checked(0, layer.clipping_mask)
|
||||||
|
clipping_mask_icon.visible = layer.clipping_mask
|
||||||
if is_instance_valid(layer.parent):
|
if is_instance_valid(layer.parent):
|
||||||
if not layer.parent.is_visible_in_hierarchy():
|
if not layer.parent.is_visible_in_hierarchy():
|
||||||
visibility_button.modulate.a = 0.33
|
visibility_button.modulate.a = 0.33
|
||||||
|
@ -108,7 +112,9 @@ func _input(event: InputEvent) -> void:
|
||||||
func _on_LayerContainer_gui_input(event: InputEvent) -> void:
|
func _on_LayerContainer_gui_input(event: InputEvent) -> void:
|
||||||
var project := Global.current_project
|
var project := Global.current_project
|
||||||
|
|
||||||
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT:
|
if not event is InputEventMouseButton:
|
||||||
|
return
|
||||||
|
if event.button_index == MOUSE_BUTTON_LEFT:
|
||||||
Global.canvas.selection.transform_content_confirm()
|
Global.canvas.selection.transform_content_confirm()
|
||||||
var prev_curr_layer := project.current_layer
|
var prev_curr_layer := project.current_layer
|
||||||
if Input.is_action_pressed(&"shift"):
|
if Input.is_action_pressed(&"shift"):
|
||||||
|
@ -135,6 +141,10 @@ func _on_LayerContainer_gui_input(event: InputEvent) -> void:
|
||||||
line_edit.visible = true
|
line_edit.visible = true
|
||||||
line_edit.editable = true
|
line_edit.editable = true
|
||||||
line_edit.grab_focus()
|
line_edit.grab_focus()
|
||||||
|
elif event.button_index == MOUSE_BUTTON_RIGHT and event.pressed:
|
||||||
|
var layer := Global.current_project.layers[layer_index]
|
||||||
|
if not layer is GroupLayer:
|
||||||
|
popup_menu.popup(Rect2(get_global_mouse_position(), Vector2.ONE))
|
||||||
|
|
||||||
|
|
||||||
func _on_LineEdit_focus_exited() -> void:
|
func _on_LineEdit_focus_exited() -> void:
|
||||||
|
@ -368,3 +378,12 @@ func _get_region_rect(y_begin: float, y_end: float) -> Rect2:
|
||||||
rect.position.y += rect.size.y * y_begin
|
rect.position.y += rect.size.y * y_begin
|
||||||
rect.size.y *= y_end - y_begin
|
rect.size.y *= y_end - y_begin
|
||||||
return rect
|
return rect
|
||||||
|
|
||||||
|
|
||||||
|
func _on_popup_menu_id_pressed(id: int) -> void:
|
||||||
|
var layer := Global.current_project.layers[layer_index]
|
||||||
|
if id == 0:
|
||||||
|
layer.clipping_mask = not layer.clipping_mask
|
||||||
|
popup_menu.set_item_checked(0, layer.clipping_mask)
|
||||||
|
clipping_mask_icon.visible = layer.clipping_mask
|
||||||
|
Global.canvas.draw_layers()
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
[gd_scene load_steps=6 format=3 uid="uid://bai814sqvk68f"]
|
[gd_scene load_steps=7 format=3 uid="uid://bai814sqvk68f"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://src/UI/Timeline/LayerButton.gd" id="1_6hlpe"]
|
[ext_resource type="Script" path="res://src/UI/Timeline/LayerButton.gd" id="1_6hlpe"]
|
||||||
[ext_resource type="Texture2D" uid="uid://c2b3htff5yox8" path="res://assets/graphics/layers/layer_visible.png" id="2_ef6fb"]
|
[ext_resource type="Texture2D" uid="uid://c2b3htff5yox8" path="res://assets/graphics/layers/layer_visible.png" id="2_ef6fb"]
|
||||||
[ext_resource type="Texture2D" uid="uid://dndlglvqc7v6a" path="res://assets/graphics/layers/group_expanded.png" id="2_enrtd"]
|
[ext_resource type="Texture2D" uid="uid://dndlglvqc7v6a" path="res://assets/graphics/layers/group_expanded.png" id="2_enrtd"]
|
||||||
[ext_resource type="Texture2D" uid="uid://dhc0pnnqojd2m" path="res://assets/graphics/layers/unlock.png" id="3_ah1my"]
|
[ext_resource type="Texture2D" uid="uid://dhc0pnnqojd2m" path="res://assets/graphics/layers/unlock.png" id="3_ah1my"]
|
||||||
[ext_resource type="Texture2D" uid="uid://cofw1x6chh4i" path="res://assets/graphics/layers/unlinked_layer.png" id="4_058qm"]
|
[ext_resource type="Texture2D" uid="uid://cofw1x6chh4i" path="res://assets/graphics/layers/unlinked_layer.png" id="4_058qm"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://ieo8fsapcgsy" path="res://assets/graphics/layers/clipping_mask.png" id="6_73j5q"]
|
||||||
|
|
||||||
[node name="LayerButton" type="Button"]
|
[node name="LayerButton" type="Button"]
|
||||||
offset_right = 200.0
|
offset_right = 200.0
|
||||||
|
@ -147,6 +148,13 @@ unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
mouse_filter = 2
|
mouse_filter = 2
|
||||||
|
|
||||||
|
[node name="ClippingMask" type="TextureRect" parent="HBoxContainer/LayerName"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
visible = false
|
||||||
|
layout_mode = 2
|
||||||
|
texture = ExtResource("6_73j5q")
|
||||||
|
stretch_mode = 5
|
||||||
|
|
||||||
[node name="LayerNameLabel" type="Label" parent="HBoxContainer/LayerName"]
|
[node name="LayerNameLabel" type="Label" parent="HBoxContainer/LayerName"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
@ -169,9 +177,17 @@ caret_blink_interval = 0.5
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
mouse_filter = 2
|
mouse_filter = 2
|
||||||
|
|
||||||
|
[node name="PopupMenu" type="PopupMenu" parent="."]
|
||||||
|
disable_3d = true
|
||||||
|
item_count = 1
|
||||||
|
item_0/text = "Clipping mask"
|
||||||
|
item_0/checkable = 1
|
||||||
|
item_0/id = 0
|
||||||
|
|
||||||
[connection signal="gui_input" from="." to="." method="_on_LayerContainer_gui_input"]
|
[connection signal="gui_input" from="." to="." method="_on_LayerContainer_gui_input"]
|
||||||
[connection signal="pressed" from="HBoxContainer/LayerButtons/ExpandButton" to="." method="_on_ExpandButton_pressed"]
|
[connection signal="pressed" from="HBoxContainer/LayerButtons/ExpandButton" to="." method="_on_ExpandButton_pressed"]
|
||||||
[connection signal="pressed" from="HBoxContainer/LayerButtons/VisibilityButton" to="." method="_on_VisibilityButton_pressed"]
|
[connection signal="pressed" from="HBoxContainer/LayerButtons/VisibilityButton" to="." method="_on_VisibilityButton_pressed"]
|
||||||
[connection signal="pressed" from="HBoxContainer/LayerButtons/LockButton" to="." method="_on_LockButton_pressed"]
|
[connection signal="pressed" from="HBoxContainer/LayerButtons/LockButton" to="." method="_on_LockButton_pressed"]
|
||||||
[connection signal="pressed" from="HBoxContainer/LayerButtons/LinkButton" to="." method="_on_LinkButton_pressed"]
|
[connection signal="pressed" from="HBoxContainer/LayerButtons/LinkButton" to="." method="_on_LinkButton_pressed"]
|
||||||
[connection signal="focus_exited" from="HBoxContainer/LayerName/LayerNameLineEdit" to="." method="_on_LineEdit_focus_exited"]
|
[connection signal="focus_exited" from="HBoxContainer/LayerName/LayerNameLineEdit" to="." method="_on_LineEdit_focus_exited"]
|
||||||
|
[connection signal="id_pressed" from="PopupMenu" to="." method="_on_popup_menu_id_pressed"]
|
||||||
|
|
Loading…
Reference in a new issue