mirror of
https://github.com/Orama-Interactive/Pixelorama.git
synced 2025-03-15 07:45:19 +00:00
* Blend group layers on `DrawingAlgos.blend_layers()` * Support group layer blending on the canvas * Allow editing of group layer properties * Fix issues with group layer blending in canvas, and unite common code * Group layers can now be used as clipping masks * Make move tool preview work on child layers * Change OffsetImage's `blend_layers()` to support group layer blending * Support group layer blending in the canvas preview * Fix layer blending mode, clipping mask opacity and cel opacity not being updated automatically if the layer/cel changed is not selected * Add a pass through blending mode to layer groups Fingers crossed that no bugs were introduced * Fix issue with layers that belong to pass through groups not updating their textures on the canvas automatically on undo
145 lines
5.3 KiB
GDScript
145 lines
5.3 KiB
GDScript
extends Node2D
|
|
|
|
enum Mode { TIMELINE, SPRITESHEET }
|
|
var mode := Mode.TIMELINE
|
|
## Use this material only when the animation of the canvas preview is playing
|
|
## This way we optimize drawing when the frame being shown is the same as the main canvas
|
|
var animation_material := material as ShaderMaterial
|
|
var h_frames := 1
|
|
var v_frames := 1
|
|
var start_sprite_sheet_frame := 1
|
|
var end_sprite_sheet_frame := 1
|
|
var frame_index := 0:
|
|
set(value):
|
|
frame_index = value
|
|
if mode == Mode.SPRITESHEET:
|
|
return
|
|
if frame_index == Global.current_project.current_frame: # Animation not playing
|
|
if material != Global.canvas.material:
|
|
material = Global.canvas.material
|
|
else: # The animation of the canvas preview is playing
|
|
if material != animation_material:
|
|
material = animation_material
|
|
|
|
@onready var animation_timer := $AnimationTimer as Timer
|
|
@onready var transparent_checker = get_parent().get_node("TransparentChecker") as ColorRect
|
|
|
|
|
|
func _ready() -> void:
|
|
Global.cel_switched.connect(_cel_switched)
|
|
material = Global.canvas.material
|
|
|
|
|
|
func _draw() -> void:
|
|
var project := Global.current_project
|
|
match mode:
|
|
Mode.TIMELINE:
|
|
if frame_index >= project.frames.size():
|
|
frame_index = project.current_frame
|
|
if animation_timer.is_stopped():
|
|
frame_index = project.current_frame
|
|
var frame := project.frames[frame_index]
|
|
animation_timer.wait_time = frame.duration * (1.0 / project.fps)
|
|
# If we just use the first cel and it happens to be a GroupCel
|
|
# nothing will get drawn
|
|
var cel_to_draw := Global.current_project.find_first_drawable_cel(frame)
|
|
# Placeholder so we can have a material here
|
|
if is_instance_valid(cel_to_draw):
|
|
draw_texture(cel_to_draw.image_texture, Vector2.ZERO)
|
|
if material == animation_material:
|
|
# Only use a unique material if the animation of the canvas preview is playing
|
|
# Otherwise showing a different frame than the main canvas is impossible
|
|
_draw_layers()
|
|
Mode.SPRITESHEET:
|
|
var image := project.frames[project.current_frame].cels[0].get_image()
|
|
var slices := _split_spritesheet(image, h_frames, v_frames)
|
|
# Limit start and end
|
|
if end_sprite_sheet_frame > slices.size():
|
|
end_sprite_sheet_frame = slices.size()
|
|
if start_sprite_sheet_frame < 0:
|
|
start_sprite_sheet_frame = 0
|
|
if frame_index >= end_sprite_sheet_frame:
|
|
frame_index = start_sprite_sheet_frame - 1
|
|
var src_rect := slices[frame_index]
|
|
var rect := Rect2(Vector2.ZERO, src_rect.size)
|
|
# If we just use the first cel and it happens to be a GroupCel
|
|
# nothing will get drawn
|
|
var cel_to_draw := Global.current_project.find_first_drawable_cel()
|
|
# Placeholder so we can have a material here
|
|
if is_instance_valid(cel_to_draw):
|
|
draw_texture_rect_region(cel_to_draw.image_texture, rect, src_rect)
|
|
transparent_checker.fit_rect(rect)
|
|
|
|
|
|
func _draw_layers() -> void:
|
|
var project := Global.current_project
|
|
var current_frame := project.frames[frame_index]
|
|
var current_cels := current_frame.cels
|
|
var textures: Array[Image] = []
|
|
# Nx4 texture, where N is the number of layers and the first row are the blend modes,
|
|
# the second are the opacities, the third are the origins and the fourth are the
|
|
# clipping mask booleans.
|
|
var metadata_image := Image.create(project.layers.size(), 4, false, Image.FORMAT_R8)
|
|
# Draw current frame layers
|
|
for i in project.ordered_layers:
|
|
var cel := current_cels[i]
|
|
var layer := project.layers[i]
|
|
var cel_image: Image
|
|
if layer is GroupLayer and layer.blend_mode != BaseLayer.BlendModes.PASS_THROUGH:
|
|
cel_image = layer.blend_children(
|
|
current_frame, Vector2i.ZERO, Global.display_layer_effects
|
|
)
|
|
else:
|
|
if Global.display_layer_effects:
|
|
cel_image = layer.display_effects(cel)
|
|
else:
|
|
cel_image = cel.get_image()
|
|
textures.append(cel_image)
|
|
DrawingAlgos.set_layer_metadata_image(layer, cel, metadata_image, i)
|
|
var texture_array := Texture2DArray.new()
|
|
texture_array.create_from_images(textures)
|
|
material.set_shader_parameter("layers", texture_array)
|
|
material.set_shader_parameter("metadata", ImageTexture.create_from_image(metadata_image))
|
|
|
|
|
|
func _on_AnimationTimer_timeout() -> void:
|
|
match mode:
|
|
Mode.TIMELINE:
|
|
var project := Global.current_project
|
|
var first_frame := 0
|
|
var last_frame := project.frames.size() - 1
|
|
|
|
if Global.play_only_tags:
|
|
for tag in project.animation_tags:
|
|
if project.current_frame + 1 >= tag.from && project.current_frame + 1 <= tag.to:
|
|
first_frame = tag.from - 1
|
|
last_frame = mini(project.frames.size() - 1, tag.to - 1)
|
|
|
|
if frame_index < last_frame:
|
|
frame_index += 1
|
|
else:
|
|
frame_index = first_frame
|
|
animation_timer.wait_time = project.frames[frame_index].duration * (1.0 / project.fps)
|
|
|
|
Mode.SPRITESHEET:
|
|
frame_index += 1
|
|
animation_timer.wait_time = 1.0 / Global.current_project.fps
|
|
animation_timer.set_one_shot(true)
|
|
animation_timer.start()
|
|
queue_redraw()
|
|
|
|
|
|
func _cel_switched() -> void:
|
|
queue_redraw()
|
|
|
|
|
|
func _split_spritesheet(image: Image, horiz: int, vert: int) -> Array[Rect2]:
|
|
var result: Array[Rect2] = []
|
|
horiz = mini(horiz, image.get_size().x)
|
|
vert = mini(vert, image.get_size().y)
|
|
var frame_width := image.get_size().x / horiz
|
|
var frame_height := image.get_size().y / vert
|
|
for yy in range(vert):
|
|
for xx in range(horiz):
|
|
result.append(Rect2(frame_width * xx, frame_height * yy, frame_width, frame_height))
|
|
return result
|