1
0
Fork 0
mirror of https://github.com/Orama-Interactive/Pixelorama.git synced 2025-01-30 23:19:49 +00:00

Add cel properties and z-index to individual cels

This commit is contained in:
Emmanouil Papadeas 2023-12-06 03:22:33 +02:00
parent f893e68d59
commit c0a8202145
10 changed files with 146 additions and 49 deletions

View file

@ -1815,12 +1815,15 @@ msgstr ""
msgid "Unlink Cels"
msgstr ""
msgid "Frame Properties"
msgid "Properties"
msgstr ""
msgid "Frame properties"
msgstr ""
msgid "Cel properties"
msgstr ""
#. Found on the popup menu that appears when a user right-clicks on a frame button. When clicked, the order of the selected frames is being reversed.
msgid "Reverse Frames"
msgstr ""

View file

@ -21,22 +21,23 @@ func blend_layers(
# the second are the opacities and the third are the origins
var metadata_image := Image.create(project.layers.size(), 3, false, Image.FORMAT_R8)
for i in project.layers.size():
var layer := project.layers[i]
var ordered_index := project.ordered_layers[i]
var layer := project.layers[ordered_index]
var include := true if layer.is_visible_in_hierarchy() else false
if only_selected and include:
var test_array := [project.frames.find(frame), i]
if not test_array in project.selected_cels:
include = false
var cel := frame.cels[i]
var cel := frame.cels[ordered_index]
var cel_image := layer.display_effects(cel)
textures.append(cel_image)
# Store the blend mode
metadata_image.set_pixel(i, 0, Color(layer.blend_mode / 255.0, 0.0, 0.0, 0.0))
metadata_image.set_pixel(ordered_index, 0, Color(layer.blend_mode / 255.0, 0.0, 0.0, 0.0))
# Store the opacity
if include:
metadata_image.set_pixel(i, 1, Color(cel.opacity, 0.0, 0.0, 0.0))
metadata_image.set_pixel(ordered_index, 1, Color(cel.opacity, 0.0, 0.0, 0.0))
else:
metadata_image.set_pixel(i, 1, Color())
metadata_image.set_pixel(ordered_index, 1, Color())
var texture_array := Texture2DArray.new()
texture_array.create_from_images(textures)
var params := {

View file

@ -1,9 +1,9 @@
class_name BaseCel
extends RefCounted
## Base class for cel properties.
## The term "cel" comes from "celluloid" (https://en.wikipedia.org/wiki/Cel).
## "Cel" is short for the term "celluloid" [url]https://en.wikipedia.org/wiki/Cel[/url].
signal texture_changed ## Emitted whenever cel's tecture is changed
signal texture_changed ## Emitted whenever the cel's texture is changed
var opacity := 1.0 ## Opacity/Transparency of the cel.
## The image stored in the cel.
@ -14,6 +14,7 @@ var image_texture: Texture2D:
## [br] If the cel is not linked then it is [code]null[/code].
var link_set = null # { "cels": Array, "hue": float } or null
var transformed_content: Image ## Used in transformations (moving, scaling etc with selections).
var z_index := 0
# Methods to Override:
@ -70,12 +71,14 @@ func update_texture() -> void:
## Returns a curated [Dictionary] containing the cel data.
func serialize() -> Dictionary:
return {"opacity": opacity}
return {"opacity": opacity, "z_index": z_index}
## Sets the cel data according to a curated [Dictionary] obtained from [method serialize].
func deserialize(dict: Dictionary) -> void:
opacity = dict["opacity"]
if dict.has("z_index"):
z_index = dict["z_index"]
## Used to perform cleanup after a cel is removed.

View file

@ -33,14 +33,15 @@ var frames: Array[Frame] = []
var layers: Array[BaseLayer] = []
var current_frame := 0
var current_layer := 0
var selected_cels := [[0, 0]] # Array of Arrays of 2 integers (frame & layer)
var selected_cels := [[0, 0]] ## Array of Arrays of 2 integers (frame & layer)
var ordered_layers: Array[int] = [0]
var animation_tags: Array[AnimationTag] = []:
set = _animation_tags_changed
var guides: Array[Guide] = []
var brushes: Array[Image] = []
var reference_images: Array[ReferenceImage] = []
var vanishing_points := [] # Array of Vanishing Points
var vanishing_points := [] ## Array of Vanishing Points
var fps := 6.0
var x_symmetry_point: float
@ -49,15 +50,15 @@ var x_symmetry_axis := SymmetryGuide.new()
var y_symmetry_axis := SymmetryGuide.new()
var selection_map := SelectionMap.new()
# This is useful for when the selection is outside of the canvas boundaries,
# on the left and/or above (negative coords)
## This is useful for when the selection is outside of the canvas boundaries,
## on the left and/or above (negative coords)
var selection_offset := Vector2i.ZERO:
set(value):
selection_offset = value
Global.canvas.selection.marching_ants_outline.offset = selection_offset
var has_selection := false
# For every camera (currently there are 3)
## For every camera (currently there are 3)
var cameras_rotation: PackedFloat32Array = [0.0, 0.0, 0.0]
var cameras_zoom: PackedVector2Array = [
Vector2(0.15, 0.15), Vector2(0.15, 0.15), Vector2(0.15, 0.15)
@ -436,6 +437,7 @@ func deserialize(dict: Dictionary) -> void:
if dict.has("fps"):
fps = dict.fps
_deserialize_metadata(self, dict)
order_layers()
func _serialize_metadata(object: Object) -> Dictionary:
@ -613,6 +615,25 @@ func find_first_drawable_cel(frame := frames[current_frame]) -> BaseCel:
return result
func order_layers(frame_index := current_frame) -> void:
ordered_layers = []
for i in layers.size():
ordered_layers.append(i)
ordered_layers.sort_custom(_z_index_sort.bind(frame_index))
func _z_index_sort(a: int, b: int, frame_index: int) -> bool:
var z_index_a := frames[frame_index].cels[a].z_index
var z_index_b := frames[frame_index].cels[b].z_index
var layer_index_a := layers[a].index + z_index_a
var layer_index_b := layers[b].index + z_index_b
if layer_index_a < layer_index_b:
return true
if layer_index_a == layer_index_b and z_index_a < z_index_b:
return true
return false
# Timeline modifications
# Modifying layers or frames Arrays on the current project should generally only be done
# through these methods.
@ -832,6 +853,7 @@ func _update_frame_ui() -> void:
## Update the layer indices and layer/cel buttons
func _update_layer_ui() -> void:
order_layers()
for l in layers.size():
layers[l].index = l
Global.layer_vbox.get_child(layers.size() - 1 - l).layer_index = l

View file

@ -29,6 +29,8 @@ var layer_metadata_texture := ImageTexture.new()
func _ready() -> void:
material.set_shader_parameter("layers", layer_texture_array)
material.set_shader_parameter("metadata", layer_metadata_texture)
Global.project_changed.connect(queue_redraw)
onion_past.type = onion_past.PAST
onion_past.blue_red_color = Global.onion_skinning_past_color
@ -124,7 +126,7 @@ func update_texture(layer_i: int, frame_i := -1, project := Global.current_proje
cel_image.get_size()
== Vector2i(layer_texture_array.get_width(), layer_texture_array.get_height())
):
layer_texture_array.update_layer(cel_image, layer_i)
layer_texture_array.update_layer(cel_image, project.ordered_layers[layer_i])
func update_selected_cels_textures(project := Global.current_project) -> void:
@ -146,60 +148,72 @@ func draw_layers() -> void:
)
if recreate_texture_array:
var textures: Array[Image] = []
textures.resize(project.layers.size())
# Nx3 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
layer_metadata_image = Image.create(project.layers.size(), 3, false, Image.FORMAT_RG8)
# Draw current frame layers
for i in project.layers.size():
var ordered_index := project.ordered_layers[i]
var layer := project.layers[i]
var cel := current_cels[i]
var cel_image: Image
if Global.display_layer_effects:
cel_image = layer.display_effects(current_cels[i])
cel_image = layer.display_effects(cel)
else:
cel_image = current_cels[i].get_image()
textures.append(cel_image)
cel_image = cel.get_image()
textures[ordered_index] = cel_image
# Store the blend mode
layer_metadata_image.set_pixel(i, 0, Color(layer.blend_mode / 255.0, 0.0, 0.0, 0.0))
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():
layer_metadata_image.set_pixel(i, 1, Color(current_cels[i].opacity, 0.0, 0.0, 0.0))
layer_metadata_image.set_pixel(ordered_index, 1, Color(cel.opacity, 0.0, 0.0, 0.0))
else:
layer_metadata_image.set_pixel(i, 1, Color())
layer_metadata_image.set_pixel(ordered_index, 1, Color())
# Store the origin
if [project.current_frame, i] in project.selected_cels:
var origin := Vector2(move_preview_location).abs() / Vector2(cel_image.get_size())
layer_metadata_image.set_pixel(i, 2, Color(origin.x, origin.y, 0.0, 0.0))
layer_metadata_image.set_pixel(
ordered_index, 2, Color(origin.x, origin.y, 0.0, 0.0)
)
else:
layer_metadata_image.set_pixel(i, 2, Color())
layer_metadata_image.set_pixel(ordered_index, 2, Color())
layer_texture_array.create_from_images(textures)
layer_metadata_texture.set_image(layer_metadata_image)
else: # Update the TextureArray
if layer_texture_array.get_layers() > 0:
for i in project.layers.size():
var layer := project.layers[i]
var test_array := [project.current_frame, i]
if not update_all_layers:
var test_array := [project.current_frame, i]
if not test_array in project.selected_cels:
continue
var ordered_index := project.ordered_layers[i]
var layer := project.layers[i]
var cel := current_cels[i]
var cel_image: Image
if Global.display_layer_effects:
cel_image = layer.display_effects(cel)
else:
cel_image = cel.get_image()
layer_texture_array.update_layer(cel_image, i)
layer_metadata_image.set_pixel(i, 0, Color(layer.blend_mode / 255.0, 0.0, 0.0, 0.0))
layer_texture_array.update_layer(cel_image, ordered_index)
layer_metadata_image.set_pixel(
ordered_index, 0, Color(layer.blend_mode / 255.0, 0.0, 0.0, 0.0)
)
if layer.is_visible_in_hierarchy():
layer_metadata_image.set_pixel(i, 1, Color(cel.opacity, 0.0, 0.0, 0.0))
layer_metadata_image.set_pixel(
ordered_index, 1, Color(cel.opacity, 0.0, 0.0, 0.0)
)
else:
layer_metadata_image.set_pixel(i, 1, Color())
layer_metadata_image.set_pixel(ordered_index, 1, Color())
var origin := Vector2(move_preview_location).abs() / Vector2(cel_image.get_size())
layer_metadata_image.set_pixel(i, 2, Color(origin.x, origin.y, 0.0, 0.0))
layer_metadata_image.set_pixel(
ordered_index, 2, Color(origin.x, origin.y, 0.0, 0.0)
)
layer_metadata_texture.update(layer_metadata_image)
material.set_shader_parameter("layers", layer_texture_array)
material.set_shader_parameter("metadata", layer_metadata_texture)
material.set_shader_parameter("origin_x_positive", move_preview_location.x > 0)
material.set_shader_parameter("origin_y_positive", move_preview_location.y > 0)
update_all_layers = false

View file

@ -80,7 +80,7 @@ func _draw_layers() -> void:
# the second are the opacities and the third are the origins
var metadata_image := Image.create(project.layers.size(), 3, false, Image.FORMAT_R8)
# Draw current frame layers
for i in project.layers.size():
for i in project.ordered_layers:
if current_cels[i] is GroupCel:
continue
var layer := project.layers[i]

View file

@ -1,6 +1,6 @@
extends Button
enum MenuOptions { DELETE, LINK, UNLINK, PROPERTIES }
enum MenuOptions { PROPERTIES, DELETE, LINK, UNLINK }
var frame := 0
var layer := 0
@ -10,6 +10,7 @@ var cel: BaseCel
@onready var linked_indicator: Polygon2D = get_node_or_null("LinkedIndicator")
@onready var cel_texture: TextureRect = $CelTexture
@onready var transparent_checker: ColorRect = $CelTexture/TransparentChecker
@onready var properties: AcceptDialog = $Properties
func _ready() -> void:
@ -98,6 +99,8 @@ func _on_CelButton_pressed() -> void:
func _on_PopupMenu_id_pressed(id: int) -> void:
match id:
MenuOptions.PROPERTIES:
properties.popup_centered()
MenuOptions.DELETE:
_delete_cel_content()
@ -294,3 +297,14 @@ func _get_region_rect(x_begin: float, x_end: float) -> Rect2:
rect.position.x += rect.size.x * x_begin
rect.size.x *= x_end - x_begin
return rect
func _on_z_index_slider_value_changed(value: float) -> void:
cel.z_index = value
Global.current_project.order_layers()
Global.canvas.update_all_layers = true
Global.canvas.queue_redraw()
func _on_properties_visibility_changed() -> void:
Global.dialog_open(properties.visible)

View file

@ -1,8 +1,9 @@
[gd_scene load_steps=5 format=3 uid="uid://dw7ci3uixjuev"]
[gd_scene load_steps=6 format=3 uid="uid://dw7ci3uixjuev"]
[ext_resource type="Script" path="res://src/UI/Timeline/CelButton.gd" id="1_iewgo"]
[ext_resource type="PackedScene" uid="uid://3pmb60gpst7b" path="res://src/UI/Nodes/TransparentChecker.tscn" id="2_mi8wp"]
[ext_resource type="Shader" path="res://src/Shaders/TransparentChecker.gdshader" id="3_qv21g"]
[ext_resource type="Script" path="res://src/UI/Nodes/ValueSlider.gd" id="4_wcpcc"]
[sub_resource type="ShaderMaterial" id="1"]
shader = ExtResource("3_qv21g")
@ -53,13 +54,15 @@ grow_horizontal = 2
grow_vertical = 2
[node name="PopupMenu" type="PopupMenu" parent="."]
item_count = 3
item_0/text = "Delete"
item_0/id = -1
item_1/text = "Link Cels to"
item_1/id = -1
item_2/text = "Unlink Cels"
item_count = 4
item_0/text = "Properties"
item_0/id = 0
item_1/text = "Delete"
item_1/id = 1
item_2/text = "Link Cels to"
item_2/id = 2
item_3/text = "Unlink Cels"
item_3/id = 3
[node name="LinkedIndicator" type="Polygon2D" parent="."]
color = Color(0, 1, 0, 1)
@ -67,6 +70,43 @@ invert_enabled = true
invert_border = 1.0
polygon = PackedVector2Array(0, 0, 36, 0, 36, 36, 0, 36)
[node name="Properties" type="AcceptDialog" parent="."]
title = "Cel properties"
size = Vector2i(300, 100)
exclusive = false
popup_window = true
[node name="GridContainer" type="GridContainer" parent="Properties"]
offset_left = 8.0
offset_top = 8.0
offset_right = 292.0
offset_bottom = 55.0
columns = 2
[node name="Label" type="Label" parent="Properties/GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
text = "Z-Index:"
[node name="ZIndexSlider" type="TextureProgressBar" parent="Properties/GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
focus_mode = 2
mouse_default_cursor_shape = 2
theme_type_variation = &"ValueSlider"
min_value = -64.0
max_value = 64.0
allow_greater = true
allow_lesser = true
nine_patch_stretch = true
stretch_margin_left = 3
stretch_margin_top = 3
stretch_margin_right = 3
stretch_margin_bottom = 3
script = ExtResource("4_wcpcc")
[connection signal="pressed" from="." to="." method="_on_CelButton_pressed"]
[connection signal="resized" from="." to="." method="_on_CelButton_resized"]
[connection signal="id_pressed" from="PopupMenu" to="." method="_on_PopupMenu_id_pressed"]
[connection signal="visibility_changed" from="Properties" to="." method="_on_properties_visibility_changed"]
[connection signal="value_changed" from="Properties/GridContainer/ZIndexSlider" to="." method="_on_z_index_slider_value_changed"]

View file

@ -1,6 +1,6 @@
extends Button
enum { REMOVE, CLONE, MOVE_LEFT, MOVE_RIGHT, PROPERTIES, REVERSE, CENTER }
enum { PROPERTIES, REMOVE, CLONE, MOVE_LEFT, MOVE_RIGHT, REVERSE, CENTER }
var frame := 0

View file

@ -14,19 +14,19 @@ script = ExtResource("1")
[node name="PopupMenu" type="PopupMenu" parent="."]
item_count = 7
item_0/text = "Remove Frame"
item_0/text = "Properties"
item_0/id = -1
item_0/disabled = true
item_1/text = "Clone Frame"
item_1/text = "Remove Frame"
item_1/id = -1
item_2/text = "Move Left"
item_1/disabled = true
item_2/text = "Clone Frame"
item_2/id = -1
item_2/disabled = true
item_3/text = "Move Right"
item_3/text = "Move Left"
item_3/id = -1
item_3/disabled = true
item_4/text = "Frame Properties"
item_4/text = "Move Right"
item_4/id = -1
item_4/disabled = true
item_5/text = "Reverse Frames"
item_5/id = 5
item_5/disabled = true